Separate video profiles and network bitrate from encoder VBV
This commit is contained in:
1 parent
a06bbc34df
commit
520f7a29a7
2 files changed
+52
-29
No files matched your search
+38
-22
@@ -42,11 +42,11 @@ static const struct video_level *level_find(int codec, uint8_t id) {
|
|||||||
}
|
}
|
||||||
static int limit_valid(const struct call_video_limit *l, int codec) {
|
static int limit_valid(const struct call_video_limit *l, int codec) {
|
||||||
if (!l->width && !l->height && !l->fps && !l->pixels_per_second
|
if (!l->width && !l->height && !l->fps && !l->pixels_per_second
|
||||||
&& !l->maximum_bps && !l->maximum_vbv_bits && !l->level) return 1;
|
&& !l->maximum_bps && !l->profiles && !l->level) return 1;
|
||||||
return l->width >= 16 && l->width <= 4096 && l->height >= 16 && l->height <= 4096
|
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->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->maximum_bps >= CALL_VIDEO_RATE_MIN_BPS && l->maximum_bps <= CALL_VIDEO_RATE_MAX_BPS
|
||||||
&& l->maximum_vbv_bits && l->maximum_vbv_bits <= 16000000 && level_find(codec, l->level);
|
&& l->profiles && !(l->profiles & ~(codec == CALL_VIDEO_H264 ? 3u : 1u)) && level_find(codec, l->level);
|
||||||
}
|
}
|
||||||
int call_video_caps_valid(const struct call_video_caps *caps) {
|
int call_video_caps_valid(const struct call_video_caps *caps) {
|
||||||
if (!caps) return 0;
|
if (!caps) return 0;
|
||||||
@@ -56,7 +56,7 @@ int call_video_caps_valid(const struct call_video_caps *caps) {
|
|||||||
static int fits(const struct call_video_limit *l, const struct call_video_format *f) {
|
static int fits(const struct call_video_limit *l, const struct call_video_format *f) {
|
||||||
return f->width <= l->width && f->height <= l->height && f->fps <= l->fps
|
return f->width <= l->width && f->height <= l->height && f->fps <= l->fps
|
||||||
&& (uint64_t)f->width * f->height * f->fps <= l->pixels_per_second && f->level <= l->level
|
&& (uint64_t)f->width * f->height * f->fps <= l->pixels_per_second && f->level <= l->level
|
||||||
&& f->maximum_bps <= l->maximum_bps && f->vbv_bits <= l->maximum_vbv_bits;
|
&& f->maximum_bps <= l->maximum_bps && (f->profile & l->profiles);
|
||||||
}
|
}
|
||||||
int call_video_choose(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
int call_video_choose(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
||||||
struct call_video_format *format) {
|
struct call_video_format *format) {
|
||||||
@@ -65,27 +65,31 @@ int call_video_choose(const struct call_video_caps *sender, const struct call_vi
|
|||||||
}
|
}
|
||||||
struct call_video_format f = *format;
|
struct call_video_format f = *format;
|
||||||
// Проверяем вход независимо от наличия codec у сторон, на самом высоком уровне.
|
// Проверяем вход независимо от наличия codec у сторон, на самом высоком уровне.
|
||||||
f.codec = CALL_VIDEO_H265; f.level = 186;
|
f.codec = CALL_VIDEO_H265; f.level = 186; f.profile = CALL_VIDEO_MAIN;
|
||||||
if (!call_video_format_valid(&f)) {
|
if (!call_video_format_valid(&f)) {
|
||||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video choose invalid size=%ux%u fps=%u bitrate=%u vbv=%u",
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "video choose invalid size=%ux%u fps=%u bitrate=%u profile=%u",
|
||||||
f.width, f.height, f.fps, f.maximum_bps, f.vbv_bits);
|
f.width, f.height, f.fps, f.maximum_bps, format->profile);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
for (int codec = 2; codec >= 1; codec--) {
|
for (int codec = 2; codec >= 1; codec--) {
|
||||||
if (format->codec && codec != format->codec) continue;
|
if (format->codec && codec != format->codec) continue;
|
||||||
f.codec = codec;
|
f.codec = codec;
|
||||||
|
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;
|
||||||
const struct video_level *levels = codec == CALL_VIDEO_H264 ? avc_levels : hevc_levels;
|
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);
|
unsigned count = codec == CALL_VIDEO_H264 ? sizeof(avc_levels) / sizeof(*levels) : sizeof(hevc_levels) / sizeof(*levels);
|
||||||
for (unsigned i = 0; i < count; i++) {
|
for (unsigned i = 0; i < count; i++) {
|
||||||
f.level = levels[i].id;
|
f.level = levels[i].id;
|
||||||
if (!call_video_format_valid(&f) || !fits(&sender->encode[codec - 1], &f) || !fits(&receiver->decode[codec - 1], &f)) continue;
|
if (!call_video_format_valid(&f) || !fits(&sender->encode[codec - 1], &f) || !fits(&receiver->decode[codec - 1], &f)) continue;
|
||||||
*format = f;
|
*format = f;
|
||||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video selected codec=%u level=%u size=%ux%u fps=%u bitrate_cap=%u vbv=%u",
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video selected codec=%u level=%u size=%ux%u fps=%u bitrate_cap=%u profile=%u",
|
||||||
f.codec, f.level, f.width, f.height, f.fps, f.maximum_bps, f.vbv_bits); return codec;
|
f.codec, f.level, f.width, f.height, f.fps, f.maximum_bps, f.profile); return codec;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video no common mode codec=%u size=%ux%u fps=%u bitrate_cap=%u vbv=%u",
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "video no common mode codec=%u size=%ux%u fps=%u bitrate_cap=%u profile=%u",
|
||||||
format->codec, f.width, f.height, f.fps, f.maximum_bps, f.vbv_bits);
|
format->codec, f.width, f.height, f.fps, f.maximum_bps, format->profile);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int call_video_format_valid(const struct call_video_format *f) {
|
int call_video_format_valid(const struct call_video_format *f) {
|
||||||
@@ -93,14 +97,27 @@ int call_video_format_valid(const struct call_video_format *f) {
|
|||||||
&& f->width <= 4096 && f->height <= 4096 && !(f->width % 2) && !(f->height % 2)
|
&& 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->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->maximum_bps >= CALL_VIDEO_RATE_MIN_BPS && f->maximum_bps <= CALL_VIDEO_RATE_MAX_BPS
|
||||||
&& f->vbv_bits && f->vbv_bits <= 16000000 && (uint64_t)f->vbv_bits * f->fps >= f->maximum_bps)) return 0;
|
&& (f->profile == 1 || (f->codec == CALL_VIDEO_H264 && f->profile == 2)))) return 0;
|
||||||
const struct video_level *l = level_find(f->codec, f->level);
|
const struct video_level *l = level_find(f->codec, f->level);
|
||||||
if (!l) return 0;
|
if (!l) return 0;
|
||||||
// AVC — macroblocks; HEVC резервируем padding до CTU 64, чтобы не зависеть от backend min-CU.
|
// AVC — macroblocks; HEVC резервируем padding до CTU 64, чтобы не зависеть от backend min-CU.
|
||||||
uint64_t w = f->codec == CALL_VIDEO_H264 ? (f->width + 15u) / 16u : (f->width + 63u) / 64u * 64u;
|
uint64_t w = f->codec == CALL_VIDEO_H264 ? (f->width + 15u) / 16u : (f->width + 63u) / 64u * 64u;
|
||||||
uint64_t h = f->codec == CALL_VIDEO_H264 ? (f->height + 15u) / 16u : (f->height + 63u) / 64u * 64u;
|
uint64_t h = f->codec == CALL_VIDEO_H264 ? (f->height + 15u) / 16u : (f->height + 63u) / 64u * 64u;
|
||||||
return w * h <= l->picture && w * h * f->fps <= l->rate && w * w <= 8ull * l->picture && h * h <= 8ull * l->picture
|
return w * h <= l->picture && w * h * f->fps <= l->rate && w * w <= 8ull * l->picture && h * h <= 8ull * l->picture
|
||||||
&& f->maximum_bps <= (uint64_t)l->kbps * 1000 && f->vbv_bits <= (uint64_t)l->cpb_kbits * 1000;
|
&& f->maximum_bps <= (uint64_t)l->kbps * 1000;
|
||||||
|
}
|
||||||
|
unsigned call_video_dpb_limit(int codec, unsigned level, unsigned width, unsigned height) {
|
||||||
|
const struct video_level *l = level <= 255 ? level_find(codec, level) : NULL;
|
||||||
|
if (!l || !width || !height || width > 4096 || height > 4096) return 0;
|
||||||
|
if (codec == CALL_VIDEO_H265) {
|
||||||
|
uint64_t size = (uint64_t)width * height;
|
||||||
|
if (size > l->picture) return 0;
|
||||||
|
return size <= l->picture / 4 ? 16 : size <= l->picture / 2 ? 12 : size <= l->picture * 3 / 4 ? 8 : 6;
|
||||||
|
}
|
||||||
|
static const unsigned dpb_mbs[] = {396, 900, 2376, 2376, 2376, 4752, 8100, 8100, 18000,
|
||||||
|
20480, 32768, 32768, 34816, 110400, 184320, 184320, 696320, 696320, 696320};
|
||||||
|
unsigned frames = dpb_mbs[l - avc_levels] / (((width + 15) / 16) * ((height + 15) / 16));
|
||||||
|
return frames < 16 ? frames : 16;
|
||||||
}
|
}
|
||||||
int call_video_format_supported(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
int call_video_format_supported(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
||||||
const struct call_video_format *f) {
|
const struct call_video_format *f) {
|
||||||
@@ -110,7 +127,7 @@ int call_video_format_supported(const struct call_video_caps *sender, const stru
|
|||||||
int call_video_same_format(const struct call_video_format *a, const struct call_video_format *b) {
|
int call_video_same_format(const struct call_video_format *a, const struct call_video_format *b) {
|
||||||
return a->generation == b->generation && a->width == b->width && a->height == b->height
|
return a->generation == b->generation && a->width == b->width && a->height == b->height
|
||||||
&& a->fps == b->fps && a->rotation == b->rotation && a->codec == b->codec && a->level == b->level
|
&& a->fps == b->fps && a->rotation == b->rotation && a->codec == b->codec && a->level == b->level
|
||||||
&& a->maximum_bps == b->maximum_bps && a->vbv_bits == b->vbv_bits;
|
&& a->maximum_bps == b->maximum_bps && a->profile == b->profile;
|
||||||
}
|
}
|
||||||
int call_video_caps_encode(const struct call_video_caps *caps, uint8_t out[CALL_VIDEO_CAPS_BYTES]) {
|
int call_video_caps_encode(const struct call_video_caps *caps, uint8_t out[CALL_VIDEO_CAPS_BYTES]) {
|
||||||
if (!out || !call_video_caps_valid(caps)) {
|
if (!out || !call_video_caps_valid(caps)) {
|
||||||
@@ -118,10 +135,10 @@ int call_video_caps_encode(const struct call_video_caps *caps, uint8_t out[CALL_
|
|||||||
}
|
}
|
||||||
for (unsigned i = 0; i < 4; i++) {
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
const struct call_video_limit *l = i < 2 ? &caps->encode[i] : &caps->decode[i - 2];
|
const struct call_video_limit *l = i < 2 ? &caps->encode[i] : &caps->decode[i - 2];
|
||||||
uint8_t *p = out + i * 20;
|
uint8_t *p = out + i * 16;
|
||||||
write_be(p, l->width, 2); write_be(p + 2, l->height, 2);
|
write_be(p, l->width, 2); write_be(p + 2, l->height, 2);
|
||||||
write_be(p + 4, l->fps, 2); write_be(p + 6, l->pixels_per_second, 4);
|
write_be(p + 4, l->fps, 2); write_be(p + 6, l->pixels_per_second, 4);
|
||||||
write_be(p + 10, l->maximum_bps, 4); write_be(p + 14, l->maximum_vbv_bits, 4); p[18] = l->level; p[19] = 0;
|
write_be(p + 10, l->maximum_bps, 4); p[14] = l->level; p[15] = l->profiles;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -132,11 +149,10 @@ int call_video_caps_parse(const uint8_t *data, size_t length, struct call_video_
|
|||||||
struct call_video_caps caps = {0};
|
struct call_video_caps caps = {0};
|
||||||
for (unsigned i = 0; i < 4; i++) {
|
for (unsigned i = 0; i < 4; i++) {
|
||||||
struct call_video_limit *l = i < 2 ? &caps.encode[i] : &caps.decode[i - 2];
|
struct call_video_limit *l = i < 2 ? &caps.encode[i] : &caps.decode[i - 2];
|
||||||
const uint8_t *p = data + i * 20;
|
const uint8_t *p = data + i * 16;
|
||||||
l->width = read_be(p, 2); l->height = read_be(p + 2, 2);
|
l->width = read_be(p, 2); l->height = read_be(p + 2, 2);
|
||||||
l->fps = read_be(p + 4, 2); l->pixels_per_second = read_be(p + 6, 4);
|
l->fps = read_be(p + 4, 2); l->pixels_per_second = read_be(p + 6, 4);
|
||||||
l->maximum_bps = read_be(p + 10, 4); l->maximum_vbv_bits = read_be(p + 14, 4); l->level = p[18];
|
l->maximum_bps = read_be(p + 10, 4); l->level = p[14]; l->profiles = p[15];
|
||||||
if (p[19]) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "video capabilities reserved byte index=%u", i); return -1; }
|
|
||||||
}
|
}
|
||||||
if (!call_video_caps_valid(&caps)) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "video capabilities invalid limits"); return -1; }
|
if (!call_video_caps_valid(&caps)) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "video capabilities invalid limits"); return -1; }
|
||||||
*out = caps; return 0;
|
*out = caps; return 0;
|
||||||
@@ -154,9 +170,9 @@ int call_video_fragment_encode(const struct call_video_frame *f, uint8_t *out, s
|
|||||||
write_be(out + 16, f->frame_id, 8); write_be(out + 24, f->pts_us, 8);
|
write_be(out + 16, f->frame_id, 8); write_be(out + 24, f->pts_us, 8);
|
||||||
write_be(out + 32, f->format.width, 2); write_be(out + 34, f->format.height, 2);
|
write_be(out + 32, f->format.width, 2); write_be(out + 34, f->format.height, 2);
|
||||||
write_be(out + 36, f->format.fps, 2); write_be(out + 38, f->format.rotation, 2);
|
write_be(out + 36, f->format.fps, 2); write_be(out + 38, f->format.rotation, 2);
|
||||||
out[40] = f->format.codec; out[41] = f->flags; out[42] = f->format.level; out[43] = 0;
|
out[40] = f->format.codec; out[41] = f->flags; out[42] = f->format.level; out[43] = f->format.profile;
|
||||||
write_be(out + 44, f->size, 4); write_be(out + 48, f->offset, 4);
|
write_be(out + 44, f->size, 4); write_be(out + 48, f->offset, 4);
|
||||||
write_be(out + 52, f->format.maximum_bps, 4); write_be(out + 56, f->format.vbv_bits, 4);
|
write_be(out + 52, f->format.maximum_bps, 4);
|
||||||
memcpy(out + CALL_VIDEO_HEADER_BYTES, f->data, f->length);
|
memcpy(out + CALL_VIDEO_HEADER_BYTES, f->data, f->length);
|
||||||
*length = CALL_VIDEO_HEADER_BYTES + f->length; return 0;
|
*length = CALL_VIDEO_HEADER_BYTES + f->length; return 0;
|
||||||
}
|
}
|
||||||
@@ -171,9 +187,9 @@ int call_video_fragment_parse(const uint8_t *data, size_t length, struct call_vi
|
|||||||
f.format.fps = read_be(data + 36, 2); f.format.rotation = read_be(data + 38, 2);
|
f.format.fps = read_be(data + 36, 2); f.format.rotation = read_be(data + 38, 2);
|
||||||
f.format.codec = data[40]; f.flags = data[41]; f.format.level = data[42];
|
f.format.codec = data[40]; f.flags = data[41]; f.format.level = data[42];
|
||||||
f.size = read_be(data + 44, 4); f.offset = read_be(data + 48, 4);
|
f.size = read_be(data + 44, 4); f.offset = read_be(data + 48, 4);
|
||||||
f.format.maximum_bps = read_be(data + 52, 4); f.format.vbv_bits = read_be(data + 56, 4);
|
f.format.maximum_bps = read_be(data + 52, 4); f.format.profile = data[43];
|
||||||
f.length = length - CALL_VIDEO_HEADER_BYTES; f.data = data + CALL_VIDEO_HEADER_BYTES;
|
f.length = length - CALL_VIDEO_HEADER_BYTES; f.data = data + CALL_VIDEO_HEADER_BYTES;
|
||||||
if (data[43] || !fragment_valid(&f)) {
|
if (!fragment_valid(&f)) {
|
||||||
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video fragment invalid fields frame=%llu size=%u offset=%u bytes=%u",
|
DEBUG_WARN(DEBUG_CATEGORY_CALL, "video fragment invalid fields frame=%llu size=%u offset=%u bytes=%u",
|
||||||
(unsigned long long)f.frame_id, f.size, f.offset, f.length); return -1;
|
(unsigned long long)f.frame_id, f.size, f.offset, f.length); return -1;
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-7
@@ -1,6 +1,6 @@
|
|||||||
/* Общий контракт видеотрека. Все операции выполняет владелец в uasync-потоке.
|
/* Общий контракт видеотрека. Все операции выполняет владелец в uasync-потоке.
|
||||||
* Кодеки: H.264 Constrained Baseline / H.265 Main (Main tier), 8 bit, 4:2:0,
|
* Кодеки: H.264 Baseline/Constrained Baseline, H.265 Main (Main tier), 8 bit,
|
||||||
* progressive, один reference, без B-кадров. Profile/tier фиксированы протоколом.
|
* 4:2:0, progressive, без B-кадров/reorder. DPB ограничен уровнем потока.
|
||||||
* Поколение меняется только при смене формата/камеры; bitrate не меняет поколение.
|
* Поколение меняется только при смене формата/камеры; bitrate не меняет поколение.
|
||||||
* KEY: config_length:4BE + Annex B configuration + Annex B access unit (как uAV2).
|
* KEY: config_length:4BE + Annex B configuration + Annex B access unit (как uAV2).
|
||||||
* Приём хранит один частичный кадр. Готовый view заимствован до следующего push/reset.
|
* Приём хранит один частичный кадр. Готовый view заимствован до следующего push/reset.
|
||||||
@@ -18,8 +18,11 @@ extern "C" {
|
|||||||
#define CALL_VIDEO_KEY 1
|
#define CALL_VIDEO_KEY 1
|
||||||
#define CALL_VIDEO_MAX_FRAME (2u * 1024u * 1024u)
|
#define CALL_VIDEO_MAX_FRAME (2u * 1024u * 1024u)
|
||||||
#define CALL_VIDEO_FRAGMENT_BYTES 1200u
|
#define CALL_VIDEO_FRAGMENT_BYTES 1200u
|
||||||
#define CALL_VIDEO_HEADER_BYTES 60u
|
#define CALL_VIDEO_HEADER_BYTES 56u
|
||||||
#define CALL_VIDEO_CAPS_BYTES 80u
|
#define CALL_VIDEO_CAPS_BYTES 64u
|
||||||
|
#define CALL_VIDEO_BASELINE 1u
|
||||||
|
#define CALL_VIDEO_CONSTRAINED_BASELINE 2u
|
||||||
|
#define CALL_VIDEO_MAIN 1u
|
||||||
|
|
||||||
/* Нулевой limit означает отсутствие проверенного encoder/decoder данного профиля.
|
/* Нулевой limit означает отсутствие проверенного encoder/decoder данного профиля.
|
||||||
* Индексы 0=H.264, 1=H.265. Лимиты независимы для каждого направления.
|
* Индексы 0=H.264, 1=H.265. Лимиты независимы для каждого направления.
|
||||||
@@ -28,7 +31,8 @@ extern "C" {
|
|||||||
* выбранный режим дополнительно проверяется открытием платформенного codec. */
|
* выбранный режим дополнительно проверяется открытием платформенного codec. */
|
||||||
struct call_video_limit {
|
struct call_video_limit {
|
||||||
uint16_t width, height, fps;
|
uint16_t width, height, fps;
|
||||||
uint32_t pixels_per_second, maximum_bps, maximum_vbv_bits;
|
uint32_t pixels_per_second, maximum_bps;
|
||||||
|
uint8_t profiles; // Маска профилей данного codec; decode Baseline включает CB явно.
|
||||||
uint8_t level;
|
uint8_t level;
|
||||||
};
|
};
|
||||||
struct call_video_caps { struct call_video_limit encode[2], decode[2]; };
|
struct call_video_caps { struct call_video_limit encode[2], decode[2]; };
|
||||||
@@ -36,7 +40,8 @@ struct call_video_format {
|
|||||||
uint64_t generation;
|
uint64_t generation;
|
||||||
uint16_t width, height, fps, rotation;
|
uint16_t width, height, fps, rotation;
|
||||||
uint8_t codec, level;
|
uint8_t codec, level;
|
||||||
uint32_t maximum_bps, vbv_bits; // Верхний bitrate и размер VBV неизменны внутри generation.
|
uint32_t maximum_bps; // Предел целевого bitrate; не обещание мгновенного размера выхода/CPB.
|
||||||
|
uint8_t profile;
|
||||||
};
|
};
|
||||||
struct call_video_frame {
|
struct call_video_frame {
|
||||||
uint64_t call_id, frame_id;
|
uint64_t call_id, frame_id;
|
||||||
@@ -48,7 +53,7 @@ struct call_video_frame {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int call_video_caps_valid(const struct call_video_caps *caps);
|
int call_video_caps_valid(const struct call_video_caps *caps);
|
||||||
/* format задаёт generation/размер/FPS/rotation/maximum_bps/vbv_bits, level=0.
|
/* format задаёт generation/размер/FPS/rotation/maximum_bps, level=0, profile=0 для auto.
|
||||||
* codec=0: H.265, затем H.264. Успех: codec (1/2), заполняются codec и минимальный level.
|
* codec=0: H.265, затем H.264. Успех: codec (1/2), заполняются codec и минимальный level.
|
||||||
* 0 — нет совместимого режима, -1 — неверные аргументы; на отказе format не меняется. */
|
* 0 — нет совместимого режима, -1 — неверные аргументы; на отказе format не меняется. */
|
||||||
int call_video_choose(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
int call_video_choose(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
||||||
@@ -57,6 +62,8 @@ int call_video_choose(const struct call_video_caps *sender, const struct call_vi
|
|||||||
int call_video_format_supported(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
int call_video_format_supported(const struct call_video_caps *sender, const struct call_video_caps *receiver,
|
||||||
const struct call_video_format *format);
|
const struct call_video_format *format);
|
||||||
int call_video_format_valid(const struct call_video_format *format);
|
int call_video_format_valid(const struct call_video_format *format);
|
||||||
|
/* Нормативный предел DPB в кадрах по coded geometry и level; 0 при неверном уровне. */
|
||||||
|
unsigned call_video_dpb_limit(int codec, unsigned level, unsigned width, unsigned height);
|
||||||
int call_video_same_format(const struct call_video_format *a, const struct call_video_format *b);
|
int call_video_same_format(const struct call_video_format *a, const struct call_video_format *b);
|
||||||
int call_video_caps_encode(const struct call_video_caps *caps, uint8_t out[CALL_VIDEO_CAPS_BYTES]);
|
int call_video_caps_encode(const struct call_video_caps *caps, uint8_t out[CALL_VIDEO_CAPS_BYTES]);
|
||||||
int call_video_caps_parse(const uint8_t *data, size_t length, struct call_video_caps *out);
|
int call_video_caps_parse(const uint8_t *data, size_t length, struct call_video_caps *out);
|
||||||
|
|||||||
Reference in new issue
Block a user