4. 视频录制流畅度提升
任务说明
在Android相机HAL层,视频录制帧率不稳定通常源于3A算法(自动曝光/自动白平衡/自动对焦)的频繁收敛、传感器输出帧间隔抖动以及ISP管线处理延迟波动。本案例目标是通过调整HAL层参数,使视频录制时帧率稳定在目标值(如30fps)附近,帧间隔抖动控制在±1ms以内。
核心实现
以下代码实现一个HAL层帧率稳定性控制模块,包含三个关键机制:帧间隔统计与抖动检测、3A收敛抑制、动态帧间隔补偿。该模块可直接集成到HAL的StreamingProcessor中。
// frame_rate_stabilizer.h
#ifndef FRAME_RATE_STABILIZER_H
#define FRAME_RATE_STABILIZER_H
#include <chrono>
#include <deque>
#include <mutex>
#include <cstdint>
namespace android {
namespace camera_hal {
struct FrameStabilizeConfig {
uint32_t target_fps = 30; // 目标帧率
uint32_t frame_interval_us = 33333; // 目标帧间隔(微秒)
uint32_t jitter_tolerance_us = 1000; // 抖动容忍度(±1ms)
uint32_t max_3a_converge_frames = 5; // 3A收敛期间最多跳过的帧数
bool enable_3a_stall = true; // 是否启用3A收敛抑制
};
class FrameRateStabilizer {
public:
explicit FrameRateStabilizer(const FrameStabilizeConfig& config)
: config_(config) {}
// 在HAL层每次出帧前调用,返回是否应丢弃当前帧
bool ShouldDropFrame(uint64_t frame_timestamp_us) {
std::lock_guard<std::mutex> lock(mutex_);
uint64_t now_us = frame_timestamp_us;
if (last_frame_time_us_ == 0) {
last_frame_time_us_ = now_us;
return false;
}
uint64_t actual_interval = now_us - last_frame_time_us_;
last_frame_time_us_ = now_us;
// 记录最近60帧的间隔用于统计
intervals_.push_back(actual_interval);
if (intervals_.size() > 60) {
intervals_.pop_front();
}
// 判断当前帧间隔是否超出容忍范围
int64_t delta = static_cast<int64_t>(actual_interval) -
static_cast<int64_t>(config_.frame_interval_us);
if (delta < 0) delta = -delta;
// 帧间隔过短(帧率过高),丢弃当前帧以稳定节奏
if (delta > config_.jitter_tolerance_us &&
actual_interval < config_.frame_interval_us) {
dropped_frames_++;
return true;
}
return false;
}
// 3A收敛期间调用,抑制不必要的曝光/白平衡调整
void On3AConverging(bool is_converging) {
std::lock_guard<std::mutex> lock(mutex_);
if (is_converging) {
converge_frame_count_ = 0;
is_3a_active_ = true;
} else {
is_3a_active_ = false;
}
}
// 获取当前帧率稳定性统计
struct StabilityStats {
float avg_interval_us;
float jitter_us;
uint32_t dropped_frames;
uint32_t total_frames;
};
StabilityStats GetStats() {
std::lock_guard<std::mutex> lock(mutex_);
StabilityStats stats;
stats.dropped_frames = dropped_frames_;
stats.total_frames = total_frames_;
if (intervals_.empty()) {
stats.avg_interval_us = 0;
stats.jitter_us = 0;
return stats;
}
// 计算平均帧间隔
uint64_t sum = 0;
for (auto interval : intervals_) {
sum += interval;
}
stats.avg_interval_us = static_cast<float>(sum) / intervals_.size();
// 计算标准差作为抖动指标
float mean = stats.avg_interval_us;
float variance_sum = 0;
for (auto interval : intervals_) {
float diff = static_cast<float>(interval) - mean;
variance_sum += diff * diff;
}
stats.jitter_us = sqrt(variance_sum / intervals_.size());
return stats;
}
private:
FrameStabilizeConfig config_;
std::mutex mutex_;
uint64_t last_frame_time_us_ = 0;
std::deque<uint64_t> intervals_;
uint32_t dropped_frames_ = 0;
uint32_t total_frames_ = 0;
bool is_3a_active_ = false;
uint32_t converge_frame_count_ = 0;
};
} // namespace camera_hal
} // namespace android
#endif // FRAME_RATE_STABILIZER_H
集成到HAL管线
在HAL的StreamingProcessor中,按以下方式调用帧率稳定器:
// StreamingProcessor.cpp 关键片段
#include "frame_rate_stabilizer.h"
using android::camera_hal::FrameRateStabilizer;
using android::camera_hal::FrameStabilizeConfig;
// 初始化时配置
FrameStabilizeConfig config;
config.target_fps = 30;
config.frame_interval_us = 33333;
config.jitter_tolerance_us = 1000;
config.enable_3a_stall = true;
FrameRateStabilizer stabilizer_(config);
// 在3A状态变化时通知
void On3AStateChanged(bool converging) {
stabilizer_.On3AConverging(converging);
}
// 在每次出帧前调用
bool ShouldOutputFrame(uint64_t timestamp_us) {
// 3A收敛期间,前5帧允许跳过以等待稳定
if (config_.enable_3a_stall && is_3a_converging_) {
if (converge_skip_count_ < config_.max_3a_converge_frames) {
converge_skip_count_++;
return false; // 跳过当前帧
}
}
// 帧间隔抖动检测
if (stabilizer_.ShouldDropFrame(timestamp_us)) {
return false; // 丢弃过快到达的帧
}
return true;
}
验证方法
完成集成后,通过以下步骤验证帧率稳定性:
- 构建与部署:编译HAL并刷入设备,或使用模拟器运行。
- 录制测试:使用Camera2 API开启30fps视频录制,持续录制60秒,场景包含固定光照和缓慢移动物体。
- 数据采集:通过
dumpsys media.camera或HAL日志获取每帧的时间戳,计算实际帧间隔。 - 判定标准:平均帧率在29.5~30.5fps范围内,帧间隔标准差小于1000μs(即±1ms抖动),且无连续丢帧超过2帧。
若帧间隔抖动仍超出容忍范围,可调整jitter_tolerance_us参数(建议范围500~2000μs),或检查传感器驱动是否存在时钟漂移问题。