Camera2App

Introduction: 厂商平台之相机模块:采用 android5.0 中 Camera2 api+MVP 模式开发的一个相机程序,包含拍照,录像,自动调焦,Zoom 调焦,暂停录像。
More: Author   ReportBugs   
Tags:

一个 CameraX + OpenGL + MediaCodec + MediaMuxer 实现的相机 App。

当前相机 app 支持功能

  • 基础功能:

    • 前后摄像头切换(support
    • 聚焦:自动聚焦、点击聚焦(support)
    • 数字变焦:手势缩放、滑块缩放(support)
    • 闪光灯模式切换(自动、常亮、关闭)(support)
    • 图片拍照(support)
    • 视频录制 mp4,包含音频(support)
  • 美颜功能(feature,下期开发中)

    • 美白
    • 瘦脸
    • 大眼
    • 贴纸
    • 滤镜

运行效果: image

*app 技术架构

┌─────────────────────────────────────────────────────────────────┐
│                    相机+音视频录制流程架构                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐    ┌──────────────┐    ┌─────────────────┐   │
│  │   CameraX   │───▶│  OpenGL 纹理   │───▶│ 编码 Surface 渲染 │   │
│  │  相机预览    │    │   处理       │    │ (MediaCodec 输入)│   │
│  └─────────────┘    └──────────────┘    └─────────────────┘   │
│         │                                        │             │
│         │                                        ▼             │
│  ┌─────────────┐                              ┌─────────────────┐   │
│  │  预览 Surface  │                              │   视频编码器     │   │
│  │  (用户可见)  │                              │   (H.264)      │   │
│  └─────────────┘                              └─────────────────┘   │
│                                                      │             │
│  ┌─────────────┐    ┌──────────────┐              │             │
│  │ AudioRecord │───▶│  音频编码器   │─────────────┤             │
│  │   音频采集    │    │   (AAC)      │              ▼             │
│  └─────────────┘    └──────────────┘    ┌─────────────────┐   │
│                                            │   MediaMuxer    │   │
│                                            │   合成 MP4 文件    │   │
│                                            └─────────────────┘   │
└─────────────────────────────────────────────────────────────────┘

架构角色

  • camerax: 相机采集
  • opengl: 图像绘制
  • audioRecord: 音频采集
  • mediacodec: 音视频编码
  • muxer: 音视频合成 mp4

Camerax 相关的说明

Camerax 中 UseCase CameraX 提供的 supportedSizes 列表中的 Size (宽 x 高) 几乎总是基于传感器的自然方向(通常是横向,例如 4000x3000, 1920x1080)。 与手机屏幕方向(通常是竖向,例如 1080x1920, 3000x4000)相反的。

  • Preview:用于在屏幕上显示相机预览,尺寸受 SurfaceView/TextureView 大小影响,可以设置分辨率大小。
  • ImageCapture:用于拍摄高质量照片, 可以设置分辨率大小。
  • ImageAnalysis:用于实时图像处理和分析,可以设置分辨率大小。
  • VideoCapture:用于录制视频,不直接设置分辨率,通过 QualitySelector 控制质量(SD、HD、FHD、UHD)。

Camerax 的支持尺寸列表 选择一个传感器方向的 supportedSizes 的 Size,camerax 会自动旋转、裁剪、填充以适应屏幕。

ResolutionSelector:用于 ImageCapture、Preview 和 ImageAnalysis。它允许你通过宽高比(AspectRatioStrategy)和具体尺寸(ResolutionStrategy)来非常精确地控制分辨率(例如 1920x1080)。这对于静态图像分析或需要精确裁剪的预览非常重要。

ResolutionSelector 的函数:

  • setAspectRatioStrategy:专门处理宽高比匹配(它会正确处理旋转问题)。
  • setResolutionStrategy:当有多个分辨率符合宽高比时,用它来选择一个(例如选择最高的、或最接近 targetWidth x targetHeight 的)。
  • setResolutionFilter:应该只用于最后的精细过滤(例如,“去掉所有小于 100 万 像素的”)。

Camerax 中旋转角度 rotationDegrees:

  • 0 度:竖屏;
  • 90 度:顺时针旋转 90 度,横屏;
  • 180 度顺时针旋转 180 度,上下颠倒的竖屏;
  • 270 度:顺时针 270 度或者逆时针 90 度,横屏

Camerax 中视频录制

QualitySelector:专门用于 VideoCapture。视频录制不仅仅是分辨率,它还涉及编码器配置、帧率和比特率。QualitySelector 将这些复杂的配置抽象为简单的“质量”级别(如 UHD、FHD、HD、SD),这与 Android 设备的 CamcorderProfile(摄像机配置文件)紧密相关。

视频分辨率:

  • SD(标清): 720×480 (NTSC) 或 720×576 (PAL),最低的视频质量标准,文件较小,显示效果较差
  • HD(高清): 1280×720 (720p),文件大小适中,目前网络视频的常见标准
  • FHD(全高清):1920×1080 (1080p),目前大多数高清电视和显示器的标准,提供高质量的视觉体验
  • UHD(超高清): 3840×2160 (2160p/4K),文件很大,提供极致的视觉体验,下一代显示技术的标准

MediaCodec 相关的说明

MediaCodec 编码模式

  • ByteBuffer 模式:
    • 格式:COLOR_FORMAT 对应的值是 MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar( 图像格式 NV21)。
    • 操作:通过 MediaCodec.dequeueInputBuffer() 获取数据输入缓冲区,再通过 MediaCodec.queueInputBuffer() 手动将 YUV 图像传给 MediaCodec。
    • 结束标识:queueInputBuffer(..., BUFFER_FLAG_END_OF_STREAM)
  • Surface 模式(推荐使用):
    • 格式:COLOR_FORMAT 对应的值是 MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface。
    • 操作:通过 MediaCodec.createInputSurface() 创建编码数据源 Surface,再通过 OpenGL 纹理,将相机预览图像绘制到该 Surface 上。
    • 结束标识:MediaCodec.signalEndOfInputStream()

camerax + mediacodec 视频编码的实现思路:

Camera → OES Texture → FBO(美颜处理)
           ↓
        draw to FBO texture
           ↓
        draw to preview EGLSurface
           ↓
        draw to encoder EGLSurface → eglSwapBuffers() → 编码帧产生
音视频编码同轨 pts 计算

视频帧的 pts 计算 mediacodec 的 surface 模式,进行视频编码时,计算 pts 的方式有 3 种:

// 1. 基于帧率的理论时间戳
val frameTimeNs = frameIndex * 1000000000L / frameRate

// 2. 基于系统时间的实时时间戳  
val elapsedTimeNs = (System.nanoTime() - startTimeNs)

// 3. 相机硬件时间戳(推荐)
val cameraTimestampNs = surfaceTexture.getTimestamp()

接着通过EGLExt.eglPresentationTimeANDROID()设置正确的时间戳。mediacodec 编码出来的数据包,单位是微妙。

音频帧的 pts mediaCodec 的 ByteBuffer 模式,进行音频编码时,pts 的计算方式有 2 种:

// 1. 基于帧率的理论时间戳
val bytesPerSample = 2 // 16-bit = 2 字节
val channel = 1
val sampleRate = 44100
val samplesPerChannel = size / (channel * bytesPerSample)
// 计算持续时间,单位微妙
val durationUs = (1000000 * samplesPerChannel / sampleRate).toLong()

// 2. 基于系统时间的实时时间戳  
val durationUs = (System.nanoTime() - startTimeNs) / 1000

接着通过mediaCodec!!.queueInputBuffer(inputBufferIndex, 0, size, durationUs, 0)设置正确时间戳。mediacodec 编码出来的数据包,单位是微妙。

虽然 视频流中 EGL 输入纳秒,音频直接输入微秒,但 MediaCodec 输出统一为微秒。

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools