一、核心功能
视频帧适配:根据输出需求调整输入视频帧的分辨率和帧率
分辨率缩放:通过智能比例计算实现像素数优化
帧率控制:通过动态丢帧实现帧率限制
方向感知处理:独立处理横屏/竖屏视频的适配需求
对齐约束:确保输出分辨率满足源端和接收端的对齐要求
动态适应:运行时根据需求变化实时调整适配策略
二、核心算法原理
分辨率缩放算法(FindScale函数):
目标驱动:在不超过max_pixels的前提下接近target_pixels
比例因子:交替使用3/4和2/3缩放因子生成最佳比例
智能起点:根据输入分辨率选择2/3或4/4作为起始比例
最优选择:遍历所有可能比例,选择最接近目标像素数的方案
像素计算:scale_pixel_count = (num²/denom²) * input_pixels
帧率控制算法:
双限制机制:取max_framerate_request_和output_format_request_.max_fps的最小值
时间戳跟踪:FramerateController根据时间间隔决定是否丢帧
动态调整:帧率限制可在运行时动态更新
三、关键数据结构
// 分数表示法(用于分辨率缩放)
struct Fraction {
int numerator; // 分子
int denominator; // 分母
void DivideByGcd(); // 约分方法
int scale_pixel_count(int input_pixels); // 像素计算
};
// 输出格式请求
struct OutputFormatRequest {
absl::optional<std::pair<int, int>> target_landscape_aspect_ratio; // 横屏宽高比
absl::optional<int> max_landscape_pixel_count; // 横屏最大像素
absl::optional<std::pair<int, int>> target_portrait_aspect_ratio; // 竖屏宽高比
absl::optional<int> max_portrait_pixel_count; // 竖屏最大像素
absl::optional<int> max_fps; // 最大帧率
};
// 视频适配器核心类
class VideoAdapter {
// 状态跟踪
int frames_in_, frames_out_, frames_scaled_;
int previous_width_, previous_height_;
// 配置参数
const int source_resolution_alignment_;
int resolution_alignment_;
// 请求参数
OutputFormatRequest output_format_request_;
int resolution_request_target_pixel_count_;
int resolution_request_max_pixel_count_;
int max_framerate_request_;
};
四、核心方法详解
AdaptFrameResolution - 帧适配核心方法
bool AdaptFrameResolution(int in_width, int in_height, int64_t in_timestamp_ns,
int* cropped_width, int* cropped_height,
int* out_width, int* out_height)
工作流程:
帧计数更新(frames_in_++)
方向检测(横屏/竖屏)
帧率控制(DropFrame检查)
裁剪区域计算(保持目标宽高比)
缩放比例计算(FindScale)
对齐调整(roundUp)
输出分辨率计算
状态更新和日志记录
OnOutputFormatRequest - 格式请求处理
void OnOutputFormatRequest(const absl::optional<VideoFormat>& format)
处理逻辑:
分离横屏/竖屏参数
维护两个版本的请求(当前使用+缓存)
帧率控制器重置
OnSinkWants - 接收端需求处理
void OnSinkWants(const rtc::VideoSinkWants& sink_wants)
关键处理:
更新像素限制(target/max)
计算分辨率对齐(LCM算法)
处理requested_resolution特殊逻辑
维护格式请求的缓存机制
五、设计亮点
智能比例选择:
// 交替使用3/4和2/3缩放因子 while (current_scale.scale_pixel_count(input_pixels) > target_pixels) { if (current_scale.numerator % 3 == 0 && current_scale.denominator % 2 == 0) { // 应用2/3缩放 current_scale.numerator /= 3; current_scale.denominator /= 2; } else { // 应用3/4缩放 current_scale.numerator *= 3; current_scale.denominator *= 4; } }方向感知处理:
// 根据方向选择不同参数 if (in_width > in_height) { target_aspect_ratio = output_format_request_.target_landscape_aspect_ratio; max_pixel_count = /* 横屏最大像素 */; } else { target_aspect_ratio = output_format_request_.target_portrait_aspect_ratio; max_pixel_count = /* 竖屏最大像素 */; }对齐保证机制:
// 确保裁剪尺寸满足缩放和对齐要求 *cropped_width = roundUp(*cropped_width, scale.denominator * resolution_alignment_, in_width);请求缓存系统:
if (sink_wants.requested_resolution) { if (!stashed_output_format_request_) { // 缓存当前请求 stashed_output_format_request_ = output_format_request_; } // 使用requested_resolution覆盖 } else if (stashed_output_format_request_) { // 恢复缓存请求 output_format_request_ = *stashed_output_format_request_; }
六、典型工作流程
初始化阶段:
VideoAdapter adapter(16); // 创建16字节对齐的适配器 adapter.OnOutputFormatRequest(1280x720, 30fps); // 设置基础输出格式
运行时适配:
int cropped_w, cropped_h, out_w, out_h; if (adapter.AdaptFrameResolution(1920, 1080, timestamp, &cropped_w, &cropped_h, &out_w, &out_h)) { // 使用适配后的帧(cropped_w x cropped_h -> out_w x out_h) } else { // 帧被丢弃 }动态调整:
// 接收端需求变化 rtc::VideoSinkWants wants; wants.max_pixel_count = 640*480; wants.target_pixel_count = 320*240; wants.max_framerate_fps = 15; adapter.OnSinkWants(wants); // 立即生效
格式更新:
// 源端输出格式变化 adapter.OnOutputFormatRequest(absl::nullopt, 1920*1080, 60);
源码解析
// 查找最优缩放比例
// 输入:原始宽高、目标像素数、最大像素数、是否启用智能起始比例
// 输出:满足约束的最佳分数表示
Fraction FindScale(int input_width, int input_height,
int target_pixels, int max_pixels,
bool variable_start_scale_factor)
{
// 仅当目标像素小于输入像素时才需要缩放
if (target_pixels >= input_pixels)
return Fraction{1, 1};
// 智能起始比例选择
Fraction current_scale = Fraction{1, 1};
if (variable_start_scale_factor) {
// 当分辨率能被3整除时,起始比例为2/3
if (input_width % 3 == 0 && input_height % 3 == 0) {
current_scale = Fraction{6, 6}; // 等效2/3
}
// 当分辨率能被9整除时,起始比例为4/9
if (input_width % 9 == 0 && input_height % 9 == 0) {
current_scale = Fraction{36, 36}; // 等效4/9
}
}
// 比例迭代优化:交替使用3/4和2/3
while (当前比例计算的像素 > 目标像素) {
if (当前比例分子能被3整除 && 分母能被2整除) {
// 应用2/3缩放
current_scale.numerator /= 3;
current_scale.denominator /= 2;
} else {
// 应用3/4缩放
current_scale.numerator *= 3;
current_scale.denominator *= 4;
}
// 检查是否优于当前最佳方案
if (输出像素 <= 最大像素 && 更接近目标像素) {
更新最佳比例;
}
}
return 最佳比例;
}
// 帧适配核心方法
bool VideoAdapter::AdaptFrameResolution(/* 参数 */)
{
// 步骤1:帧率控制决策
if (需要丢帧) {
// 每90帧记录日志
if ((frames_in_ - frames_out_) % 90 == 0) {
RTC_LOG(LS_INFO) << "VAdapt Drop Frame: ...";
}
return false;
}
// 步骤2:分辨率合法性检查
if (in_width < kMinWidth || in_height < kMinHeight) {
RTC_LOG(LS_INFO) << "VAdapt Drop Frame: ...";
return false;
}
// 步骤3:基于方向的目标参数选择
absl::optional<std::pair<int, int>> target_aspect_ratio;
if (横屏) {
// 使用横屏参数
} else {
// 使用竖屏参数
}
// 步骤4:裁剪区域计算(保持宽高比)
if (需要裁剪) {
*cropped_width = std::min(in_width, (int)(in_height * 请求宽高比));
*cropped_height = std::min(in_height, (int)(in_width / 请求宽高比));
}
// 步骤5:计算最优缩放比例
Fraction scale = FindScale(/* 参数 */);
// 步骤6:分辨率对齐调整
*cropped_width = roundUp(/* 对齐到scale.denominator * alignment */);
*cropped_height = roundUp(/* 同上 */);
// 步骤7:计算输出分辨率
*out_width = *cropped_width / scale.denominator * scale.numerator;
*out_height = *cropped_height / scale.denominator * scale.numerator;
// 步骤8:状态更新和日志
if (分辨率变化) {
++adaption_changes_;
RTC_LOG(LS_INFO) << "Frame size changed: ...";
}
return true;
}
该视频适配器设计通过智能比例选择、方向感知处理、动态请求缓存等机制,实现了高效灵活的视频帧适配功能,能够有效平衡视频质量和性能需求。