Android NDK开发详解相机之CameraX 视频捕获架构

注意:CameraX VideoCapture API 并非最终版本,可能会发生变化。

本主题中的代码示例与 camera-video:1.1.0-alpha12 版本相对应。

我们非常希望听到您的反馈意见!请将您的想法告诉我们,帮助我们完善最终的 VideoCapture API!

捕获系统通常会录制视频流和音频流,对其进行压缩,对这两个流进行多路复用,然后将生成的流写入磁盘。
在这里插入图片描述

视频和音频捕获系统概念图
图 1. 视频和音频捕获系统概念图。

在 CameraX 中,用于视频捕获的解决方案是 VideoCapture 用例:

在这里插入图片描述

展示 CameraX 如何处理 VideoCapture 用例的概念图
图 2. 展示 CameraX 如何处理 VideoCapture 用例的概念图。

如图 2 所示,CameraX 视频捕获包括几个高级架构组件:

SurfaceProvider,表示视频来源。
AudioSource,表示音频来源。
用于对视频/音频进行编码和压缩的两个编码器。
用于对两个流进行多路复用的媒体复用器。
用于写出结果的文件保存器。

VideoCapture API 会对复杂的捕获引擎进行抽象化处理,为应用提供更加简单且直观的 API。

VideoCapture API 概述

VideoCapture 是一种 CameraX 用例,既可以单独使用,也可以与其他用例搭配使用。受支持的具体组合取决于相机硬件功能,不过 Preview 和 VideoCapture 这一用例组合适用于所有设备。
注意:VideoCapture 是在 CameraX 软件包内的 camera-video 库中实现的,在 1.1.0-alpha10 及更高版本中可用。

VideoCapture API 包含可与应用通信的以下对象:

VideoCapture 是顶级用例类。VideoCapture 通过 CameraSelector 和其他 CameraX 用例绑定到 LifecycleOwner。如需详细了解这些概念和用法,请参阅 CameraX 架构。
Recorder 是与 VideoCapture 紧密耦合的 VideoOutput 实现。 Recorder 用于执行视频和音频捕获操作。应用通过 Recorder 创建录制对象。
PendingRecording 会配置录制对象,同时提供启用音频和设置事件监听器等选项。您必须使用 Recorder 来创建 PendingRecording。PendingRecording 不会录制任何内容。
Recording 会执行实际录制操作。您必须使用 PendingRecording 来创建 Recording。

图 3 展示了这些对象之间的关系:
展示 VideoCapture 用例中发生的交互的示意图
在这里插入图片描述

图 3. 展示 VideoCapture 用例中发生的交互的示意图。

图例:

使用 QualitySelector 创建 Recorder。
使用其中一个 OutputOptions 配置 Recorder。
如果需要,使用 withAudioEnabled() 启用音频。
使用 VideoRecordEvent 监听器调用 start() 以开始录制。
针对 Recording 使用 pause()/resume()/stop() 来控制录制操作。
在事件监听器内响应 VideoRecordEvents。

详细的 API 列表位于源代码内的 current-txt 中。

使用 VideoCapture API

如需将 CameraX VideoCapture 用例集成到您的应用中,请执行以下操作:

绑定 VideoCapture。
准备和配置录制。
开始和控制运行时录制。

后面的部分概述了您可以在每个步骤中执行哪些操作,以获取端到端录制会话。

绑定 VideoCapture

如需绑定 VideoCapure 用例,请执行以下操作:

创建一个 Recorder 对象。
创建 VideoCapture 对象。
绑定到 Lifecycle。

CameraX VideoCapture API 遵循构建器设计模式。应用使用 Recorder.Builder 来创建 Recorder。您还可以通过 QualitySelector 对象为 Recorder 配置视频分辨率。

CameraX Recorder 支持以下预定义的视频分辨率 Qualities:

Quality.UHD,适用于 4K 超高清视频大小 (2160p)
Quality.FHD,适用于全高清视频大小 (1080p)
Quality.HD,适用于高清视频大小 (720p)
Quality.SD,适用于标清视频大小 (480p)

请注意,获得应用授权后,CameraX 还可以选择其他分辨率。

每个选项对应的确切视频大小取决于相机和编码器的功能。如需了解详情,请参阅 CamcorderProfile 的文档。

应用可以通过创建 QualitySelector 来配置分辨率。您可以使用以下方法之一创建 QualitySelector:

使用 fromOrderedList() 提供几个首选分辨率,并包含一个后备策略,以备在不支持任何首选分辨率时使用。

CameraX 可以根据所选相机的功能确定最佳后备匹配项。如需了解详情,请参阅 QualitySelector 的 FallbackStrategy specification。例如,以下代码会请求支持的最高录制分辨率;如果所有请求分辨率都不受支持,则授权 CameraX 选择最接近 Quality.SD 分辨率的分辨率:
val qualitySelector = QualitySelector.fromOrderedList(
         listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
         FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))

首先查询相机功能,然后使用 QualitySelector::from() 从受支持的分辨率中进行选择:

    val cameraInfo = cameraProvider.availableCameraInfos.filter {
        Camera2CameraInfo
        .from(it)
        .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK
    }

    val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0])
    val filteredQualities = arrayListOf (Quality.UHD, Quality.FHD, Quality.HD, Quality.SD)
                           .filter { supportedQualities.contains(it) }

    // Use a simple ListView with the id of simple_quality_list_view
    viewBinding.simpleQualityListView.apply {
        adapter = ArrayAdapter(context,
                               android.R.layout.simple_list_item_1,
                               filteredQualities.map { it.qualityToString() })

        // Set up the user interaction to manually show or hide the system UI.
        setOnItemClickListener { _, _, position, _ ->
            // Inside View.OnClickListener,
            // convert Quality.* constant to QualitySelector
            val qualitySelector = QualitySelector.from(filteredQualities[position])

            // Create a new Recorder/VideoCapture for the new quality
            // and bind to lifecycle
            val recorder = Recorder.Builder()
                .setQualitySelector(qualitySelector).build()

             // ...
        }
    }

    // A helper function to translate Quality to a string
    fun Quality.qualityToString() : String {
        return when (this) {
            Quality.UHD -> "UHD"
            Quality.FHD -> "FHD"
            Quality.HD -> "HD"
            Quality.SD -> "SD"
            else -> throw IllegalArgumentException()
        }
    }

请注意,QualitySelector.getSupportedQualities() 返回的功能肯定适用于 VideoCapture 用例或 VideoCapture 和 Preview 用例的组合。与 ImageCapture 或 ImageAnalysis 用例绑定时,如果请求的相机不支持所需的组合,CameraX 仍可能会绑定失败。

具有 QualitySelector 后,应用即可创建 VideoCapture 对象并执行绑定。请注意,此绑定与和其他用例的绑定相同:

val recorder = Recorder.Builder()
    .setExecutor(cameraExecutor).setQualitySelector(qualitySelector)
    .build()
val videoCapture = VideoCapture.withOutput(recorder)

try {
    // Bind use cases to camera
    cameraProvider.bindToLifecycle(
            this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)
} catch(exc: Exception) {
    Log.e(TAG, "Use case binding failed", exc)
}

请注意,bindToLifecycle() 会返回一个 Camera 对象。如需详细了解如何控制相机输出(如变焦和曝光),请参阅此指南
注意:目前无法配置最终的视频编解码器和容器格式。

Recorder 会选择最适合系统的格式。最常见的视频编解码器是 H.264 AVC,其容器格式为 MPEG-4。

配置和创建录制对象

应用可以通过 Recorder 创建录制对象来执行视频和音频捕获操作。应用通过执行以下操作来创建录制对象:

使用 prepareRecording() 配置 OutputOptions。
(可选)启用录音功能。
使用 start() 注册 VideoRecordEvent 监听器,并开始捕获视频。

当您调用 start() 函数时,Recorder 会返回 Recording 对象。应用可以使用此 Recording 对象完成捕获或执行其他操作,例如暂停或恢复。

Recorder 一次支持一个 Recording 对象。对前面的 Recording 对象调用 Recording.stop() 或 Recording.close() 后,您便可以开始新的录制。

我们来更详细地看看这些步骤。首先,应用使用 Recorder.prepareRecording() 为 Recorder 配置 OutputOptions。Recorder 支持以下类型的 OutputOptions:

FileDescriptorOutputOptions,用于捕获到 FileDescriptor 中。
FileOutputOptions,用于捕获到 File 中。
MediaStoreOutputOptions,用于捕获到 MediaStore 中。

无论使用哪种 OutputOptions 类型,您都能通过 setFileSizeLimit() 来设置文件大小上限。其他选项特定于单个输出类型,例如 ParcelFileDescriptor 特定于 FileDescriptorOutputOptions。

prepareRecording() 会返回 PendingRecording 对象,该对象是一个中间对象,用于创建相应的 Recording 对象。PendingRecording 是一个瞬态类,在大多数情况下应不可见,并且很少被应用缓存。

应用可以进一步配置录制对象,例如:

使用 withAudioEnabled() 启用音频。
使用 start(Executor, Consumer<VideoRecordEvent>) 注册监听器,以接收视频录制事件。

要开始录制,请调用 PendingRecording.start()。CameraX 会将 PendingRecording 转换为 Recording,将录制请求加入队列,并将新创建的 Recording 对象返回给应用。一旦在相应相机设备上开始录制,CameraX 就会发送 VideoRecordEvent.EVENT_TYPE_START 事件。

以下示例展示了如何将视频和音频录制到 MediaStore 文件中:

// Create MediaStoreOutputOptions for our recorder
val name = "CameraX-recording-" +
        SimpleDateFormat(FILENAME_FORMAT, Locale.US)
                .format(System.currentTimeMillis()) + ".mp4"
val contentValues = ContentValues().apply {
   put(MediaStore.Video.Media.DISPLAY_NAME, name)
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver,
                              MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
                              .setContentValues(contentValues)
                              .build()

// 2. Configure Recorder and Start recording to the mediaStoreOutput.
val recording = videoCapture.output
                .prepareRecording(context, mediaStoreOutput)
                .withAudioEnabled()
                .start(ContextCompat.getMainExecutor(this), captureListener)

控制活跃录制对象

您可以使用以下方法暂停、恢复和停止正在进行的 Recording:

pause,用于暂停当前的活跃录制。
resume(),用于恢复已暂停的活跃录制。
stop(),用于完成录制并清空所有关联的录制对象。

请注意,无论录制处于暂停状态还是活跃状态,您都可以调用 stop() 来终止 Recording。

如果您已使用 PendingRecording.start() 注册了 EventListener,Recording 会使用 VideoRecordEvent 进行通信。

VideoRecordEvent.EVENT_TYPE_STATUS 用于录制统计信息,例如当前文件的大小和录制的时间跨度。
VideoRecordEvent.EVENT_TYPE_FINALIZE 用于录制结果,会包含最终文件的 URI 以及任何相关错误等信息。

在您的应用收到表示录制会话成功的 EVENT_TYPE_FINALIZE 后,您就可以从 OutputOptions 中指定的位置访问捕获的视频。

其他资源

如需详细了解 CameraX,请参阅下面列出的其他资源:

“CameraX 使用入门”Codelab
官方 CameraX 示例应用
最新的 CameraX VideoCapture API 列表
CameraX 版本说明
CameraX 源代码

本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。

最后更新时间 (UTC):2022-07-15。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Android NDK开发是指利用NDK(Native Development Kit)将C/C++开发的代码编译成so库,然后通过JNI(Java Native Interface)让Java程序调用。在Android开发中,默认使用的是Android SDK进行Java语言的开发,而对于一些需要使用C/C++的高性能计算、底层操作或跨平台需求的场景,可以使用NDK进行开发。 在Android Studio中进行NDK开发相对于Eclipse来说更加方便,特别是在Android Studio 3.0及以上版本中,配置更加简化,并引入了CMake等工具,使得开发更加便捷。首先要进行NDK开发,需要配置环境,包括导入NDK、LLDB和CMake等工具。可以通过打开Android Studio的SDK Manager,选择SDK Tools,在其中选中相应的工具进行导入。 在项目的build.gradle文件中,可以配置一些NDK相关的参数,例如编译版本、ABI过滤器等。其中,可以通过externalNativeBuild配置CMake的相关设置,包括CMakeLists.txt文件的路径和版本号。此外,在sourceSets.main中还可以设置jniLibs.srcDirs,指定so库的位置。 在进行NDK开发时,可以在jni文件夹中编写C/C++代码,并通过JNI调用相关的函数。通过JNI接口,可以实现Java与C/C++之间的相互调用,从而实现跨语言的开发。 综上所述,Android NDK开发是指利用NDK将C/C++开发的代码编译成so库,并通过JNI实现与Java的相互调用。在Android Studio中进行NDK开发相对方便,可以通过配置环境和相应的参数来进行开发。<span class="em">1</span><span class="em">2</span><span class="em">3</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

五一编程

程序之路有我与你同行

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值