Probe desktop Full HD codecs and retain GPU support by size

This commit is contained in:
evgeny committed 2026-10-19 22:37:29 +02:00
1 parent 720ee64fd1
commit bf8dfeec02
2 files changed
+53 -28

No files matched your search

+34 -15
View File
@@ -15,8 +15,9 @@ extern "C" {
#include <libswscale/swscale.h>
}
namespace {
std::atomic<unsigned> gpuProfiles[2]{};
std::atomic<unsigned> gpuPipeline[2]{};
// Отдельная проверка для 720p и Full HD: CPU-резерв 1080p не отключает GPU на 720p.
std::atomic<unsigned> gpuProfiles[2][2]{};
std::atomic<unsigned> gpuPipeline[2][2]{};
QString ffError(int value) { char text[AV_ERROR_MAX_STRING_SIZE]; av_strerror(value, text, sizeof(text)); return text; }
#ifdef _WIN32
constexpr AVHWDeviceType DeviceType = AV_HWDEVICE_TYPE_D3D11VA;
@@ -56,7 +57,16 @@ struct CallVideoDecoder::Impl {
const AVCodec* implementation = avcodec_find_decoder(f.codec == CALL_VIDEO_H265 ? AV_CODEC_ID_HEVC : AV_CODEC_ID_H264);
codec = implementation ? avcodec_alloc_context3(implementation) : nullptr;
if (!codec) { failed("allocation", AVERROR(ENOMEM)); return false; }
gpu = backend == Backend::HardwareOnly || (backend == Backend::Automatic && (gpuProfiles[f.codec - 1].load() & f.profile));
unsigned verifiedPipeline = 0; bool verifiedGpu = false;
for (unsigned tier = 0; tier < 2; ++tier) {
const unsigned width = tier ? 1920 : 1280, height = tier ? 1080 : 720;
if (std::max(f.width, f.height) <= width && uint64_t(f.width) * f.height <= width * height
&& (gpuProfiles[f.codec - 1][tier].load() & f.profile)) {
verifiedGpu = true; verifiedPipeline = std::max(verifiedPipeline, gpuPipeline[f.codec - 1][tier].load());
break;
}
}
gpu = backend == Backend::HardwareOnly || (backend == Backend::Automatic && verifiedGpu);
codec->thread_count = gpu ? 1 : 4; codec->thread_type = FF_THREAD_SLICE;
codec->flags |= AV_CODEC_FLAG_LOW_DELAY; codec->pkt_timebase = {1, 1000000};
if (gpu) {
@@ -68,7 +78,7 @@ struct CallVideoDecoder::Impl {
int result = av_hwdevice_ctx_create(&codec->hw_device_ctx, DeviceType, nullptr, nullptr, 0);
if (result < 0) { failed("hardware device", result); return false; }
codec->opaque = this; codec->get_format = getFormat;
pipeline = gpuPipeline[f.codec - 1].load();
pipeline = verifiedPipeline;
}
int result = avcodec_open2(codec, implementation, nullptr);
if (result < 0) { failed("open", result); return false; }
@@ -225,19 +235,22 @@ call_video_caps CallVideoDecoder::capabilities() {
static std::once_flag once;
std::call_once(once, [] {
for (unsigned codec = 1; codec <= 2; ++codec) {
unsigned profiles = 0, hardwareProfiles = 0, pipeline = 0;
unsigned profiles = 0, maximumTier = 0;
for (unsigned profile = 1; profile <= (codec == 1 ? 2u : 1u); ++profile) {
for (unsigned tier = 0; tier < 2; ++tier) {
const unsigned width = tier ? 1920 : 1280, height = tier ? 1080 : 720;
for (Backend backend : {Backend::HardwareOnly, Backend::SoftwareOnly}) {
bool bothOrientations = true;
unsigned backendPipeline = 0;
for (int portrait = 0; portrait <= 1 && bothOrientations; ++portrait) {
uint8_t bytes[8192]; int size = call_video_probe_key(codec, profile, portrait, bytes, sizeof(bytes));
const unsigned w = portrait ? height : width, h = portrait ? width : height;
uint8_t bytes[8192]; int size = call_video_probe_key(codec, profile, w, h, bytes, sizeof(bytes));
if (size < 0) { bothOrientations = false; break; }
CallVideoDecoder decoder(backend);
call_video_frame input{}; input.call_id = 1; input.flags = CALL_VIDEO_KEY;
input.data = bytes; input.size = input.length = size;
input.format = {1, (uint16_t)(portrait ? 720 : 1280), (uint16_t)(portrait ? 1280 : 720), 30, 0,
(uint8_t)codec, (uint8_t)(codec == 1 ? 31 : 93), 4000000, (uint8_t)profile};
input.format = {1, (uint16_t)w, (uint16_t)h, 30, 0,
(uint8_t)codec, (uint8_t)(codec == 1 ? (tier ? 40 : 31) : (tier ? 120 : 93)), 4000000, (uint8_t)profile};
unsigned sent = 0, received = 0; bool valid = true, outputBeforeEos = false;
const int64_t start = call_video_playout_now_us();
while (valid && received < 8 && call_video_playout_now_us() - start < 1500000) {
@@ -257,23 +270,29 @@ call_video_caps CallVideoDecoder::capabilities() {
if (valid && received < 8) QThread::usleep(1000);
}
valid = valid && received == 8 && outputBeforeEos;
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video decoder probe codec=%u profile=%u portrait=%d backend=%d valid=%d inputs=%u outputs=%u pipeline=%u",
codec, profile, portrait, int(backend), valid, sent, received, decoder.pipelineFrames());
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video decoder probe codec=%u profile=%u size=%ux%u backend=%d valid=%d inputs=%u outputs=%u pipeline=%u",
codec, profile, w, h, int(backend), valid, sent, received, decoder.pipelineFrames());
bothOrientations = valid; backendPipeline = std::max(backendPipeline, decoder.pipelineFrames());
}
if (bothOrientations) {
profiles |= profile;
if (backend == Backend::HardwareOnly) { hardwareProfiles |= profile; pipeline = std::max(pipeline, backendPipeline); }
maximumTier = std::max(maximumTier, tier);
#ifdef HAVE_FFMPEG
if (backend == Backend::HardwareOnly) {
gpuProfiles[codec - 1][tier].fetch_or(profile);
gpuPipeline[codec - 1][tier].store(std::max(gpuPipeline[codec - 1][tier].load(), backendPipeline));
}
#endif
break;
}
}
}
}
#ifdef HAVE_FFMPEG
gpuPipeline[codec - 1].store(pipeline); gpuProfiles[codec - 1].store(hardwareProfiles);
#endif
if (profiles) {
call_video_limit limit{1280, 720, 30, 1280 * 720 * 30, 4000000, (uint8_t)profiles, (uint8_t)(codec == 1 ? 31 : 93)};
const uint16_t width = maximumTier ? 1920 : 1280, height = maximumTier ? 1080 : 720;
call_video_limit limit{width, height, 30, uint32_t(width) * height * 30, 4000000, (uint8_t)profiles,
(uint8_t)(codec == 1 ? (maximumTier ? 40 : 31) : (maximumTier ? 120 : 93))};
if (!call_video_limit_orientations(&limit, codec)) caps.decode[codec - 1] = limit;
}
}
+19 -13
View File
@@ -256,19 +256,25 @@ void CallVideoEngine::transmit() {
call_video_caps caps = CallVideoDecoder::capabilities();
// Не связываем приём с наличием собственного encoder.
for (unsigned codec = 1; codec <= 2 && m->running; ++codec) {
call_video_format probe{1, 1280, 720, 30, 0, (uint8_t)codec, (uint8_t)(codec == 1 ? 31 : 93), 4000000,
(uint8_t)(codec == 1 ? CALL_VIDEO_CONSTRAINED_BASELINE : CALL_VIDEO_MAIN)};
bool bothOrientations = true;
for (int portrait = 0; portrait <= 1 && bothOrientations && m->running; ++portrait) {
probe.width = portrait ? 720 : 1280; probe.height = portrait ? 1280 : 720;
CallVideoEncoder encoder;
bothOrientations = encoder.open(probe, 1000000);
if (!bothOrientations) DEBUG_WARN(DEBUG_CATEGORY_CALL, "call codec probe unavailable codec=%u size=%ux%u: %s",
codec, probe.width, probe.height, qPrintable(encoder.error()));
}
if (bothOrientations && m->running) {
call_video_limit limit{1280, 720, 30, 1280 * 720 * 30, 4000000, probe.profile, probe.level};
if (!call_video_limit_orientations(&limit, codec)) caps.encode[codec - 1] = limit;
for (const QSize size : {QSize(1920, 1080), QSize(1280, 720)}) {
const bool fullhd = size.width() == 1920;
call_video_format probe{1, (uint16_t)size.width(), (uint16_t)size.height(), 30, 0, (uint8_t)codec,
(uint8_t)(codec == 1 ? (fullhd ? 40 : 31) : (fullhd ? 120 : 93)), 4000000,
(uint8_t)(codec == 1 ? CALL_VIDEO_CONSTRAINED_BASELINE : CALL_VIDEO_MAIN)};
bool bothOrientations = true;
for (int portrait = 0; portrait <= 1 && bothOrientations && m->running; ++portrait) {
probe.width = portrait ? size.height() : size.width(); probe.height = portrait ? size.width() : size.height();
CallVideoEncoder encoder;
bothOrientations = encoder.open(probe, 1000000);
if (!bothOrientations) DEBUG_WARN(DEBUG_CATEGORY_CALL, "call codec probe unavailable codec=%u size=%ux%u: %s",
codec, probe.width, probe.height, qPrintable(encoder.error()));
}
if (bothOrientations && m->running) {
call_video_limit limit{(uint16_t)size.width(), (uint16_t)size.height(), 30,
(uint32_t)(size.width() * size.height() * 30), 4000000, probe.profile, probe.level};
if (!call_video_limit_orientations(&limit, codec)) caps.encode[codec - 1] = limit;
break;
}
}
}
if (!m->running) return;