You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
4.6 KiB
77 lines
4.6 KiB
/* Реальный RX worker и decoder: коррекции аудиочасов не разрывают цепочку P-кадров. */ |
|
#include "../src/call_video_engine.h" |
|
#include "../src/call_video_encoder.h" |
|
#include "../../../src/call/call_video_port.h" |
|
#include "../../../src/call/call_audio.h" |
|
#include <QCoreApplication> |
|
#include <QElapsedTimer> |
|
#include <QThread> |
|
#include <atomic> |
|
#include <chrono> |
|
#include <vector> |
|
#include <algorithm> |
|
extern "C" { |
|
#include "../../../lib/mem.h" |
|
#include "../../../lib/debug_config.h" |
|
} |
|
static std::vector<LiveEncodedFrame> frames; |
|
static call_video_format format{1, 640, 360, 30, 0, 1, 31, 4000000, 2}; |
|
static std::atomic<int> requests{0}, rejects{0}; |
|
static int cursor = 0; |
|
static int64_t start; |
|
static bool disturbed = false; |
|
static int64_t nowUs() { return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now().time_since_epoch()).count(); } |
|
extern "C" int __wrap_call_audio_get_clock(uint64_t, int64_t now, call_clock_snapshot* out) { |
|
*out = {}; out->valid = out->measured = 1; out->pts_us = 1000000 + now - start; |
|
out->source_epoch = (now - start) / 100000 + 1; return 0; |
|
} |
|
extern "C" int __wrap_call_video_port_open(uint64_t, const call_video_caps*) { return 0; } |
|
extern "C" void __wrap_call_video_port_close(uint64_t) {} |
|
extern "C" int __wrap_call_video_port_status(uint64_t, struct call_video_port_status* out) { *out = {}; return 0; } |
|
extern "C" void __wrap_call_video_port_request_key(uint64_t) { ++requests; } |
|
extern "C" int __wrap_call_video_port_decoded(uint64_t, uint64_t, int accepted) { if (!accepted) ++rejects; return 0; } |
|
extern "C" int __wrap_call_video_port_pull(uint64_t id, call_video_frame* out) { |
|
const int64_t received = start + cursor * 33333ll + (disturbed && cursor == 30 ? 20000 : 0); |
|
if (cursor >= (int)frames.size() || nowUs() < received) return 0; |
|
// Кадр уже пришёл: остановка RX worker не должна подменить время доступности данных. |
|
if (disturbed && cursor == 60) QThread::msleep(80); |
|
const auto& f = frames[cursor]; *out = {}; out->call_id = id; out->format = format; |
|
out->pts_us = f.pts; out->frame_id = ++cursor; out->flags = f.key ? CALL_VIDEO_KEY : 0; |
|
out->received_us = received; |
|
out->size = out->length = f.data.size(); auto* data = static_cast<uint8_t*>(u_malloc(out->size)); |
|
memcpy(data, f.data.constData(), out->size); out->data = data; return 1; |
|
} |
|
int main(int argc, char** argv) { |
|
QCoreApplication app(argc, argv); debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); |
|
debug_set_category_level(DEBUG_CATEGORY_CALL, DEBUG_LEVEL_DEBUG); |
|
CallVideoEncoder encoder; if (!encoder.open(format, 1000000)) return 2; |
|
QImage image(640, 360, QImage::Format_RGB32); image.fill(Qt::blue); |
|
for (int i = 0; i < 90; ++i) { |
|
LiveEncodedFrame frame; CallVideoRateCompletion completion; |
|
if (!encoder.encode(image, 1000000 + i * 33333ll, i == 0, frame, completion)) return 3; |
|
frames.push_back(frame); |
|
} |
|
CallVideoEngine engine; std::vector<int64_t> published; |
|
QObject::connect(&engine, &CallVideoEngine::imageChanged, [&](const QImage& frame) { |
|
if (!frame.isNull()) published.push_back(nowUs()); |
|
}); |
|
for (int scenario = 0; scenario < 2; ++scenario) { |
|
disturbed = scenario != 0; cursor = 0; published.clear(); |
|
start = nowUs(); engine.start(7); QElapsedTimer timer; timer.start(); bool uiPaused = false; |
|
while (timer.elapsed() < 3200) { |
|
if (disturbed && !uiPaused && timer.elapsed() >= 1500) { QThread::msleep(60); uiPaused = true; } |
|
app.processEvents(); QThread::msleep(2); |
|
} |
|
engine.stop(); const auto stoppedCount = published.size(); app.processEvents(); |
|
if (published.size() != stoppedCount) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "stale video publication after stop"); return 1; } |
|
std::vector<int64_t> gaps; |
|
for (size_t i = 1; i < published.size(); ++i) gaps.push_back(published[i] - published[i - 1]); |
|
std::sort(gaps.begin(), gaps.end()); |
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "CLOCK PROBE disturbed=%d shown=%zu/90 key_requests=%d decoder_rejections=%d " |
|
"gap_p50_us=%lld gap_p95_us=%lld gap_max_us=%lld audio_epochs=32", disturbed, published.size(), requests.load(), rejects.load(), |
|
(long long)(gaps.empty() ? 0 : gaps[gaps.size() / 2]), (long long)(gaps.empty() ? 0 : gaps[gaps.size() * 95 / 100]), |
|
(long long)(gaps.empty() ? 0 : gaps.back())); |
|
if (published.size() < (disturbed ? 82u : 85u) || requests || rejects) return 1; |
|
} |
|
return 0; |
|
}
|
|
|