这个系列文章我们来介绍一位海外工程师如何探索 CameraX 音视频相机技术,对于想要开始学习音视频技术的朋友,这些文章是份不错的入门资料,这是第 3 篇:CameraX 实现预览。
—— 来自公众号关键帧Keyframe的分享
1、概述
当在应用中添加预览功能时,可以使用 PreviewView,它是一个可以裁剪、缩放和旋转以正确显示的 View。当相机激活时,图像预览会流式传输到 PreviewView 内部的表面。
2、使用 PreviewView
使用 PreviewView 为 CameraX 实现预览涉及以下步骤,后续部分将详细介绍这些步骤:
- 可选地配置
CameraXConfig.Provider。 - 将
PreviewView添加到布局中。 - 请求
ProcessCameraProvider。 - 在视图创建时,检查
ProcessCameraProvider是否可用。 - 选择相机并绑定生命周期和用例。
使用 PreviewView 存在一些限制。当使用 PreviewView 时,无法执行以下操作:
- 创建
SurfaceTexture并设置到TextureView和Preview.SurfaceProvider。 - 从
TextureView获取SurfaceTexture并设置到Preview.SurfaceProvider。 - 从
SurfaceView获取Surface并设置到Preview.SurfaceProvider。
如果发生上述任何情况,则 Preview 将停止向 PreviewView 流式传输帧。
将 PreviewView 添加到布局中
以下示例展示了布局中的 PreviewView:
<FrameLayout
android:id="@+id/container">
<androidx.camera.view.PreviewView
android:id="@+id/previewView" />
</FrameLayout>
请求 CameraProvider
以下代码展示了如何请求 CameraProvider:
import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.common.util.concurrent.ListenableFuture
class MainActivity : AppCompatActivity() {
private lateinit var cameraProviderFuture: ListenableFuture<ProcessCameraProvider>
override fun onCreate(savedInstanceState: Bundle?) {
cameraProviderFuture = ProcessCameraProvider.getInstance(this)
}
}
import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.common.util.concurrent.ListenableFuture
public class MainActivity extends AppCompatActivity {
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
}
}
检查 CameraProvider 是否可用
在请求 CameraProvider 后,需在视图创建时验证其初始化是否成功。以下代码展示了如何执行此操作:
cameraProviderFuture.addListener(Runnable {
val cameraProvider = cameraProviderFuture.get()
bindPreview(cameraProvider)
}, ContextCompat.getMainExecutor(this))
cameraProviderFuture.addListener(() -> {
try {
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
bindPreview(cameraProvider);
} catch (ExecutionException | InterruptedException e) {
// 此 Future 不需要处理错误。
// 这里永远不应该到达。
}
}, ContextCompat.getMainExecutor(this));
关于此示例中使用的 bindPreview 函数的示例,请参见下一节提供的代码。
选择相机并绑定生命周期和用例
在创建并确认 CameraProvider 后,执行以下操作:
- 创建
Preview。 - 指定所需的相机
LensFacing选项。 - 将选定的相机和任何用例绑定到生命周期。
- 将
Preview连接到PreviewView。
以下代码展示了示例:
fun bindPreview(cameraProvider: ProcessCameraProvider) {
var preview: Preview = Preview.Builder()
.build()
var cameraSelector: CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
preview.setSurfaceProvider(previewView.getSurfaceProvider())
var camera = cameraProvider.bindToLifecycle(this as LifecycleOwner, cameraSelector, preview)
}
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
Preview preview = new Preview.Builder()
.build();
CameraSelector cameraSelector = new CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
Camera camera = cameraProvider.bindToLifecycle((LifecycleOwner) this, cameraSelector, preview);
}
注意,bindToLifecycle() 返回一个 Camera 对象。有关控制相机输出的更多信息(例如缩放和曝光),请参见相机输出。
现在你已完成相机预览的实现。构建应用并确认预览在应用中按预期显示和运行。
3、PreviewView 的其他控制功能
CameraX 的 PreviewView 提供了一些额外的 API,用于配置以下属性:
- 用于渲染预览流的实现模式。
- 预览图像的缩放类型。
实现模式
PreviewView 可以使用以下模式之一将预览流渲染到目标 View:
PERFORMANCE模式,默认使用SurfaceView显示视频流,但在某些情况下会回退到TextureView。SurfaceView具有专用的绘图表面,更有可能通过内部硬件合成器实现硬件覆盖层,特别是当预览视频上方没有其他 UI 元素(如按钮)时。通过硬件覆盖层渲染,视频帧可以避免 GPU 路径,从而减少平台功耗和延迟。COMPATIBLE模式,使用TextureView,与SurfaceView不同,它没有专用的绘图表面。因此,视频以混合方式渲染以便显示。在此额外步骤期间,应用可以执行额外处理,例如无限制地缩放和旋转视频。
使用 PreviewView.setImplementationMode() 选择适合应用的实现模式。如果默认的 PERFORMANCE 模式不适合你的应用,以下代码示例展示了如何设置 COMPATIBLE 模式:
// viewFinder 是一个 PreviewView 实例
viewFinder.implementationMode = PreviewView.ImplementationMode.COMPATIBLE
缩放类型
当预览视频分辨率与目标 PreviewView 的尺寸不同时,需要通过裁剪或信箱式显示(保持原始纵横比)将视频内容适配到视图。PreviewView 提供了以下 ScaleTypes 用于此目的:
FIT_CENTER、FIT_START和FIT_END用于信箱式显示。视频内容会按比例缩放(放大或缩小)到可以在目标PreviewView中显示的最大尺寸。但是,虽然整个视频帧可见,但屏幕的某些部分可能会留白。根据选择的三种缩放类型中的哪一种,视频帧会相对于目标视图的中心、起始位置或结束位置对齐。FILL_CENTER、FILL_START和FILL_END用于裁剪。如果视频与PreviewView的纵横比不匹配,只有部分内容可见,但视频会填满整个PreviewView。
CameraX 默认使用的缩放类型为 FILL_CENTER。使用 PreviewView.setScaleType() 设置最适合应用的缩放类型。以下代码示例(Kotlin)设置了 FIT_CENTER 缩放类型:
// viewFinder 是一个 PreviewView 实例
viewFinder.scaleType = PreviewView.ScaleType.FIT_CENTER
显示视频的过程包括以下步骤:
- 缩放视频:
- 对于
FIT_*缩放类型,使用min(dst.width/src.width, dst.height/src.height)缩放视频。 - 对于
FILL_*缩放类型,使用max(dst.width/src.width, dst.height/src.height)缩放视频。
- 对于
- 将缩放后的视频与目标
PreviewView对齐:- 对于
FIT_CENTER/FILL_CENTER,将缩放后的视频与目标PreviewView居中对齐。 - 对于
FIT_START/FILL_START,将缩放后的视频与目标PreviewView的左上角对齐。 - 对于
FIT_END/FILL_END,将缩放后的视频与目标PreviewView的右下角对齐。
- 对于
例如,这是一个 640×480 的源视频和一个 1920×1080 的目标 PreviewView :

过程如下:
- 使用
min(1920/640, 1080/480) = 2.25按比例缩放视频帧(保持原始纵横比),得到一个 1440×1080 的中间视频帧。 - 将 1440×1080 的视频帧与 1920×1080 的
PreviewView对齐。- 对于
FIT_CENTER,将视频帧与PreviewView窗口的 中心 对齐。PreviewView的起始和结束 240 像素列为空白。 - 对于
FIT_START,将视频帧与PreviewView窗口的 起始位置(左上角)对齐。PreviewView的结束 480 像素列为空白。 - 对于
FIT_END,将视频帧与PreviewView窗口的 结束位置(右下角)对齐。PreviewView的起始 480 像素列为空白。
- 对于
- 使用
max(1920/640, 1080/480) = 3缩放视频帧,得到一个 1920×1440 的中间视频帧(大于PreviewView的尺寸)。 - 将 1920×1440 的视频帧裁剪以适应 1920×1080 的
PreviewView窗口。- 对于
FILL_CENTER,从 1920×1440 的缩放视频的 中心 裁剪 1920×1080。视频的顶部和底部 180 行不可见。 - 对于
FILL_START,从 1920×1440 的缩放视频的 起始位置 裁剪 1920×1080。视频的底部 360 行不可见。 - 对于
FILL_END,从 1920×1440 的缩放视频的 结束位置 裁剪 1920×1080。视频的顶部 360 行不可见。
- 对于
音视频方向学习、求职,欢迎加入我们的星球
丰富的音视频知识、面试题、技术方案干货分享,还可以进行面试辅导

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/yinshipin/57864.html