Negotiate HEVC Main High tier with explicit level limits
This commit is contained in:
1 parent
5c1807a712
commit
7122d70ea6
3 files changed
+35
-9
No files matched your search
@@ -46,7 +46,8 @@ static int limit_valid(const struct call_video_limit *l, int codec) {
|
||||
return l->width >= 16 && l->width <= 4096 && l->height >= 16 && l->height <= 4096
|
||||
&& l->fps >= 1 && l->fps <= 60 && l->pixels_per_second >= 256 && l->pixels_per_second <= 4096u * 4096u * 60u
|
||||
&& l->maximum_bps >= CALL_VIDEO_RATE_MIN_BPS && l->maximum_bps <= CALL_VIDEO_RATE_MAX_BPS
|
||||
&& l->profiles && !(l->profiles & ~(codec == CALL_VIDEO_H264 ? 3u : 1u)) && level_find(codec, l->level);
|
||||
&& l->profiles && !(l->profiles & ~3u) && level_find(codec, l->level)
|
||||
&& !(codec == CALL_VIDEO_H265 && (l->profiles & CALL_VIDEO_MAIN_HIGH_TIER) && l->level < 120);
|
||||
}
|
||||
int call_video_caps_valid(const struct call_video_caps *caps) {
|
||||
if (!caps) return 0;
|
||||
@@ -71,7 +72,7 @@ static int fits(const struct call_video_limit *l, const struct call_video_format
|
||||
int call_video_choose(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
||||
struct call_video_format *format) {
|
||||
if (!call_video_caps_valid(sender) || !call_video_caps_valid(receiver) || !format || format->codec > 2
|
||||
|| format->level || format->profile > 2 || (format->codec == CALL_VIDEO_H265 && format->profile > 1)) {
|
||||
|| format->level || format->profile > 2) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video choose invalid capabilities/request"); return -1;
|
||||
}
|
||||
struct call_video_format f = *format;
|
||||
@@ -94,7 +95,8 @@ int call_video_choose(const struct call_video_caps *sender, const struct call_vi
|
||||
unsigned profiles = sender->encode[codec - 1].profiles & receiver->decode[codec - 1].profiles;
|
||||
if (format->profile) profiles &= format->profile;
|
||||
if (!profiles) continue;
|
||||
f.profile = profiles & CALL_VIDEO_CONSTRAINED_BASELINE ? CALL_VIDEO_CONSTRAINED_BASELINE : 1;
|
||||
f.profile = codec == CALL_VIDEO_H264 ? (profiles & CALL_VIDEO_CONSTRAINED_BASELINE ? CALL_VIDEO_CONSTRAINED_BASELINE : 1)
|
||||
: (profiles & CALL_VIDEO_MAIN ? CALL_VIDEO_MAIN : CALL_VIDEO_MAIN_HIGH_TIER);
|
||||
const struct video_level *levels = codec == CALL_VIDEO_H264 ? avc_levels : hevc_levels;
|
||||
unsigned count = codec == CALL_VIDEO_H264 ? sizeof(avc_levels) / sizeof(*levels) : sizeof(hevc_levels) / sizeof(*levels);
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
@@ -114,7 +116,8 @@ int call_video_format_valid(const struct call_video_format *f) {
|
||||
&& f->width <= 4096 && f->height <= 4096 && !(f->width % 2) && !(f->height % 2)
|
||||
&& f->fps >= 1 && f->fps <= 60 && (f->rotation == 0 || f->rotation == 90 || f->rotation == 180 || f->rotation == 270)
|
||||
&& f->maximum_bps >= CALL_VIDEO_RATE_MIN_BPS && f->maximum_bps <= CALL_VIDEO_RATE_MAX_BPS
|
||||
&& (f->profile == 1 || (f->codec == CALL_VIDEO_H264 && f->profile == 2)))) return 0;
|
||||
&& (f->profile == 1 || f->profile == 2))) return 0;
|
||||
if (f->codec == CALL_VIDEO_H265 && f->profile == CALL_VIDEO_MAIN_HIGH_TIER && f->level < 120) return 0;
|
||||
const struct video_level *l = level_find(f->codec, f->level);
|
||||
if (!l) return 0;
|
||||
// AVC — macroblocks; HEVC резервируем padding до CTU 64, чтобы не зависеть от backend min-CU.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* Общий контракт видеотрека. Все операции выполняет владелец в uasync-потоке.
|
||||
* Кодеки: H.264 Baseline/Constrained Baseline, H.265 Main (Main tier), 8 bit,
|
||||
* Кодеки: H.264 Baseline/Constrained Baseline, H.265 Main (Main/High tier), 8 bit,
|
||||
* 4:2:0, progressive, без B-кадров/reorder. DPB ограничен уровнем потока.
|
||||
* Поколение меняется только при смене формата/камеры; bitrate не меняет поколение.
|
||||
* KEY: config_length:4BE + Annex B configuration + Annex B access unit (как uAV2).
|
||||
@@ -23,6 +23,7 @@ extern "C" {
|
||||
#define CALL_VIDEO_BASELINE 1u
|
||||
#define CALL_VIDEO_CONSTRAINED_BASELINE 2u
|
||||
#define CALL_VIDEO_MAIN 1u
|
||||
#define CALL_VIDEO_MAIN_HIGH_TIER 2u
|
||||
|
||||
/* Нулевой limit означает отсутствие проверенного encoder/decoder данного профиля.
|
||||
* Индексы 0=H.264, 1=H.265. Лимиты независимы для каждого направления.
|
||||
@@ -32,7 +33,7 @@ extern "C" {
|
||||
struct call_video_limit {
|
||||
uint16_t width, height, fps;
|
||||
uint32_t pixels_per_second, maximum_bps;
|
||||
uint8_t profiles; // Маска профилей данного codec; decode Baseline включает CB явно.
|
||||
uint8_t profiles; // Маска режимов: AVC Baseline/CB; HEVC Main с Main/High tier. Приём каждого проверяется отдельно.
|
||||
uint8_t level;
|
||||
};
|
||||
struct call_video_caps { struct call_video_limit encode[2], decode[2]; };
|
||||
|
||||
@@ -96,8 +96,8 @@ static int hevc_ptl(struct bits *b, struct call_video_bitstream *s) {
|
||||
unsigned space = get(b, 2), tier = get(b, 1), profile = get(b, 5);
|
||||
get(b, 32); // profile compatibility; принимаем только явный Main.
|
||||
get(b, 1); unsigned interlaced = get(b, 1); get(b, 2);
|
||||
get(b, 32); get(b, 12); s->level = get(b, 8); s->profile = CALL_VIDEO_MAIN;
|
||||
if (space || tier || profile != 1 || interlaced) {
|
||||
get(b, 32); get(b, 12); s->level = get(b, 8); s->profile = tier ? CALL_VIDEO_MAIN_HIGH_TIER : CALL_VIDEO_MAIN;
|
||||
if (space || (tier && s->level < 120) || profile != 1 || interlaced) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video HEVC PTL rejected space=%u profile=%u tier=%u level=%u interlaced=%u",
|
||||
space, profile, tier, s->level, interlaced); return -1;
|
||||
}
|
||||
@@ -343,10 +343,32 @@ int call_video_probe_key(int codec, int profile, unsigned width, unsigned height
|
||||
size = codec == 1 ? sizeof(call_video_fullhd_1920_1080_1) : sizeof(call_video_fullhd_1920_1080_2);
|
||||
}
|
||||
} else if (wide != 1280 || short_side != 720) return bad("probe key geometry");
|
||||
if ((codec != 1 && codec != 2) || (profile != 1 && !(codec == 1 && profile == 2))
|
||||
if ((codec != 1 && codec != 2) || (profile != 1 && profile != 2)
|
||||
|| !out || capacity < size)
|
||||
return bad("probe key arguments");
|
||||
memcpy(out, data, size);
|
||||
if (codec == 1 && profile == 1) out[10] &= ~64u;
|
||||
if (codec == CALL_VIDEO_H265 && profile == CALL_VIDEO_MAIN_HIGH_TIER) {
|
||||
// Только эталонные пробы: Main tools и Main BR/CPB также соответствуют High tier level 4.
|
||||
// Повышение декларации уровня не меняет entropy payload. Производственные VPS/SPS не переписываем.
|
||||
unsigned patched = 0;
|
||||
size_t at = start_code(out, size, 4);
|
||||
while (at < size) {
|
||||
size_t begin = at + 3, end = start_code(out, size, begin); at = end;
|
||||
unsigned type = (out[begin] >> 1) & 63;
|
||||
if (type != 32 && type != 33) continue;
|
||||
unsigned profile_at = type == 32 ? 6 : 3, raw = 0, zeros = 0;
|
||||
for (size_t i = begin; i < end; ++i) {
|
||||
if (zeros == 2 && out[i] == 3) { zeros = 0; continue; }
|
||||
if (raw == profile_at) {
|
||||
if (out[i] != 1) return bad("probe HEVC profile");
|
||||
out[i] |= 32;
|
||||
}
|
||||
if (raw == profile_at + 11) { out[i] = 120; ++patched; break; }
|
||||
zeros = out[i] == 0 ? zeros + 1 : 0; ++raw;
|
||||
}
|
||||
}
|
||||
if (patched != 2) return bad("probe HEVC PTL count");
|
||||
}
|
||||
return (int)size;
|
||||
}
|
||||
Reference in new issue
Block a user