diff --git a/tools/chatgui/src/live_video_encoder.cpp b/tools/chatgui/src/live_video_encoder.cpp index e36f3b37..899c96b3 100644 --- a/tools/chatgui/src/live_video_encoder.cpp +++ b/tools/chatgui/src/live_video_encoder.cpp @@ -163,10 +163,13 @@ bool LiveVideoEncoder::open(const LiveVideoConfig& config) { if (constrained) { c->thread_count = 1; av_dict_set(&options, "profile", name == "libx264" ? "baseline" : "main", 0); - QByteArray level = QByteArray::number(config.level / (name == "libx264" ? 10.0 : 30.0), 'f', 1); + // Оба encoder принимают level × 10 целым: дробный разбор зависит от LC_NUMERIC. + QByteArray level = QByteArray::number(name == "libx264" ? config.level : config.level / 3); params += ":ref=1:"; if (name == "libx264") params += "level=" + level; else params += "level-idc=" + level + ":high-tier=0:hrd=0:info=0:scenecut=0"; + DEBUG_DEBUG(DEBUG_CATEGORY_VIDEO, "live encoder level name=%s contract=%d parameter=%s", + name.constData(), config.level, level.constData()); } av_dict_set(&options, name == "libx264" ? "x264-params" : "x265-params", params.constData(), 0); } else if (name.endsWith("_nvenc")) { diff --git a/tools/chatgui/tests/test_call_video_encoder.cpp b/tools/chatgui/tests/test_call_video_encoder.cpp index d20d6b53..3bc0fa31 100644 --- a/tools/chatgui/tests/test_call_video_encoder.cpp +++ b/tools/chatgui/tests/test_call_video_encoder.cpp @@ -1,6 +1,8 @@ /* Настоящие encoder/decoder: отложенное переключение 4M -> 300k -> 4M, * один decoder на весь поток, непрерывные capture PTS и измеренный bitrate. */ #include +#include +#include #include "../src/call_video_encoder.h" #include "../src/call_video_decoder.h" #include "../../../src/call/call_video_bitstream.h" @@ -46,6 +48,32 @@ private slots: void switchesAtScheduledIdr_data() { QTest::addColumn("codec"); QTest::newRow("H264") << 1; QTest::newRow("H265") << 2; } + void levelIndependentOfLocale_data() { + QTest::addColumn("codec"); QTest::addColumn("locale"); + for (int codec : {1, 2}) for (const auto& locale : {QByteArray("C"), QByteArray("ru_RU.UTF-8")}) + QTest::newRow(qPrintable(QString("codec%1-%2").arg(codec).arg(QString::fromLatin1(locale)))) << codec << locale; + } + void levelIndependentOfLocale() { + QFETCH(int, codec); QFETCH(QByteArray, locale); + QByteArray previous = std::setlocale(LC_NUMERIC, nullptr); + auto restore = qScopeGuard([previous] { std::setlocale(LC_NUMERIC, previous.constData()); }); + if (!std::setlocale(LC_NUMERIC, locale.constData())) QSKIP("Requested numeric locale is not installed"); + if (!avcodec_find_encoder_by_name(codec == 1 ? "libx264" : "libx265")) QSKIP("Software encoder unavailable"); + auto config = configuration(codec); config.width = 1280; config.height = 720; + CallVideoEncoder encoder; QVERIFY2(encoder.open(config, 1000000), qPrintable(encoder.error())); + Decoder decoder; QVERIFY(decoder.open(codec)); + QImage image(config.width, config.height, QImage::Format_RGB32); image.fill(Qt::blue); + uint64_t pts = 1000000, request = 0; + for (uint32_t bitrate : {1000000u, 300000u, 4000000u}) { + QVERIFY(encoder.requestBitrate(config.generation, ++request, bitrate)); + LiveEncodedFrame output; CallVideoRateCompletion completion; + QVERIFY2(encoder.encode(image, pts, true, output, completion), qPrintable(encoder.error())); + QCOMPARE(completion.request, request); QCOMPARE(completion.bitrate, bitrate); + QVERIFY(decoder.decode(output)); QCOMPARE(decoder.context->level, int(config.level)); + QCOMPARE(decoder.frame->width, 1280); QCOMPARE(decoder.frame->height, 720); + pts += 1000000; + } + } void independentDecoders() { auto caps = CallVideoDecoder::capabilities(); QCOMPARE(caps.decode[0].profiles, uint8_t(3)); QCOMPARE(caps.decode[1].profiles, uint8_t(1));