diff --git a/tools/chatgui/src/call_video_decoder.cpp b/tools/chatgui/src/call_video_decoder.cpp index 35c52064..11b058c5 100644 --- a/tools/chatgui/src/call_video_decoder.cpp +++ b/tools/chatgui/src/call_video_decoder.cpp @@ -16,8 +16,8 @@ extern "C" { } namespace { // Отдельная проверка для 720p и Full HD: CPU-резерв 1080p не отключает GPU на 720p. -std::atomic gpuProfiles[2][2]{}; -std::atomic gpuPipeline[2][2]{}; +std::atomic gpuProfiles[2][2][2]{}; +std::atomic gpuPipeline[2][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; @@ -59,10 +59,11 @@ struct CallVideoDecoder::Impl { if (!codec) { failed("allocation", AVERROR(ENOMEM)); return false; } unsigned verifiedPipeline = 0; bool verifiedGpu = false; for (unsigned tier = 0; tier < 2; ++tier) { + const unsigned rate = f.fps > 30 ? 1 : 0; 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()); + && (gpuProfiles[f.codec - 1][tier][rate].load() & f.profile)) { + verifiedGpu = true; verifiedPipeline = std::max(verifiedPipeline, gpuPipeline[f.codec - 1][tier][rate].load()); break; } } @@ -243,10 +244,14 @@ call_video_caps CallVideoDecoder::capabilities(const std::atomic* running) if (initialized) return caps; call_video_caps probed{}; for (unsigned codec = 1; codec <= 2; ++codec) { - unsigned profiles = 0, maximumTier = 0; + call_video_limit limit{}; for (unsigned profile = 1; profile <= 2; ++profile) { for (unsigned tier = 0; tier < 2; ++tier) { + for (unsigned rate = 0; rate < 2; ++rate) { + const unsigned fps = rate ? 60 : 30; const unsigned width = tier ? 1920 : 1280, height = tier ? 1080 : 720; + const uint8_t level = codec == 1 ? (tier ? (rate ? 42 : 40) : (rate ? 32 : 31)) + : (tier ? (rate ? 123 : 120) : (rate || profile == 2 ? 120 : 93)); for (Backend backend : {Backend::HardwareOnly, Backend::SoftwareOnly}) { if (cancelled()) return {}; bool bothOrientations = true; @@ -254,27 +259,27 @@ call_video_caps CallVideoDecoder::capabilities(const std::atomic* running) for (int portrait = 0; portrait <= 1 && bothOrientations; ++portrait) { if (cancelled()) return {}; 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)); + uint8_t bytes[8192]; int size = call_video_probe_key(codec, profile, w, h, fps, 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)w, (uint16_t)h, 30, 0, - (uint8_t)codec, (uint8_t)(codec == 1 ? (tier ? 40 : 31) : (tier || profile == 2 ? 120 : 93)), + input.format = {1, (uint16_t)w, (uint16_t)h, (uint16_t)fps, 0, + (uint8_t)codec, level, 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) { if (cancelled()) return {}; - if (sent < 8 && call_video_playout_now_us() - start >= sent * 33333ll) { - input.frame_id = sent + 1; input.pts_us = 1000000 + sent * 33333ll; + if (sent < 8 && call_video_playout_now_us() - start >= sent * 1000000ll / fps) { + input.frame_id = sent + 1; input.pts_us = 1000000 + sent * 1000000ll / fps; int result = decoder.submit(input); if (result < 0) valid = false; else if (result) ++sent; } call_video_frame output{}; QImage image; int result = decoder.receive(output, image); if (result < 0) valid = false; else if (result) { - valid = output.pts_us == 1000000 + received * 33333ll; + valid = output.pts_us == 1000000 + received * 1000000ll / fps; if (!decoder.m->eos) outputBeforeEos = true; ++received; } @@ -282,29 +287,29 @@ call_video_caps CallVideoDecoder::capabilities(const std::atomic* running) if (valid && received < 8) QThread::usleep(1000); } valid = valid && received == 8 && outputBeforeEos; - 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()); + DEBUG_INFO(DEBUG_CATEGORY_CALL, "video decoder probe codec=%u profile=%u size=%ux%u fps=%u backend=%d valid=%d inputs=%u outputs=%u pipeline=%u", + codec, profile, w, h, fps, int(backend), valid, sent, received, decoder.pipelineFrames()); bothOrientations = valid; backendPipeline = std::max(backendPipeline, decoder.pipelineFrames()); } if (bothOrientations) { - profiles |= profile; - maximumTier = std::max(maximumTier, tier); + limit.width = std::max(limit.width, width); limit.height = std::max(limit.height, height); + limit.fps = std::max(limit.fps, fps); + limit.pixels_per_second = std::max(limit.pixels_per_second, width * height * fps); + limit.maximum_bps = 4000000; limit.profiles |= profile; limit.level = std::max(limit.level, level); #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)); + gpuProfiles[codec - 1][tier][rate].fetch_or(profile); + gpuPipeline[codec - 1][tier][rate].store(std::max(gpuPipeline[codec - 1][tier][rate].load(), backendPipeline)); } #endif break; } } + } } } - if (profiles) { - 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 || (profiles & CALL_VIDEO_MAIN_HIGH_TIER) ? 120 : 93))}; + if (limit.profiles) { if (!call_video_limit_orientations(&limit, codec)) probed.decode[codec - 1] = limit; } } diff --git a/tools/chatgui/src/call_video_engine.h b/tools/chatgui/src/call_video_engine.h index 4ae50db8..718413d1 100644 --- a/tools/chatgui/src/call_video_engine.h +++ b/tools/chatgui/src/call_video_engine.h @@ -13,7 +13,7 @@ public: ~CallVideoEngine() override; void start(quint64 id); void stop(); - void setVideo(bool enabled, const QByteArray& camera, int width, int height, int codec); + void setVideo(bool enabled, const QByteArray& camera, int width, int height, int codec, int fps = 30); void setPresentationDriven(bool enabled); // GUI-поток: окно подтверждает рисование, иначе достаточно доставки signal. void imagePresented(quint64 serial); // GUI-поток; повторный/старый ACK безопасно игнорируется. signals: