From bf8dfeec0207b1cf14e4c9006832e93f3f15fd76 Mon Sep 17 00:00:00 2001 From: evgeny Date: Mon, 19 Oct 2026 22:37:29 +0200 Subject: [PATCH] Probe desktop Full HD codecs and retain GPU support by size --- tools/chatgui/src/call_video_decoder.cpp | 49 ++++++++++++++++-------- tools/chatgui/src/call_video_engine.cpp | 32 +++++++++------- 2 files changed, 53 insertions(+), 28 deletions(-) diff --git a/tools/chatgui/src/call_video_decoder.cpp b/tools/chatgui/src/call_video_decoder.cpp index fda27853..5196b1a4 100644 --- a/tools/chatgui/src/call_video_decoder.cpp +++ b/tools/chatgui/src/call_video_decoder.cpp @@ -15,8 +15,9 @@ extern "C" { #include } namespace { -std::atomic gpuProfiles[2]{}; -std::atomic gpuPipeline[2]{}; +// Отдельная проверка для 720p и Full HD: CPU-резерв 1080p не отключает GPU на 720p. +std::atomic gpuProfiles[2][2]{}; +std::atomic 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; } } diff --git a/tools/chatgui/src/call_video_engine.cpp b/tools/chatgui/src/call_video_engine.cpp index 39c9398a..4826eca5 100644 --- a/tools/chatgui/src/call_video_engine.cpp +++ b/tools/chatgui/src/call_video_engine.cpp @@ -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;