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
相关产品推荐
相关产品推荐

