mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-05 19:28:46 +02:00
Squashed buildout of the tgcalls testbench: - CLI test tool with --mode p2p/reflector/group/group-churn, cross-version interop (--version, --version2), and quiet/summary output - Linux toolchain + Docker multi-stage build, AWS Fargate mass test harness, local parallel mass test harness with signaling loss simulation - SCTP writable gate, retransmission timer tuning, role-based handshake - InstanceV2CompatImpl (PeerConnection backend with V2Impl signaling) and SignalingTranslator for v14.0.0 interop - In-process Go/Pion SFU (ICE+DTLS+SRTP+SCTP per participant) with audio RTP forwarding, ActiveAudio/VideoSsrcs data channel broadcast, RTCP feedback path, and CGo c-archive integration - GroupInstanceReferenceImpl (PeerConnection group-call) and mixed-impl group mode (--reference-participants), with SDP munging for simulcast - H264 simulcast group video (FakeVideoTrackSource pattern generator, FakeVideoSink frame counting, --video flag, two-pass channel setup, reactive video setup from ActiveVideoSsrcs) - Group churn stress mode (--mode group-churn, --churn-cycles) - SFU stream-quality adaptation: BandwidthEstimator, LayerSelector state machine, RtxRingBuffer, simulcast SSRC rewrite - Transport-cc feedback generation, NetworkSimulator (delay/jitter/loss/ token-bucket bandwidth), --network-scenario step-down-up - CLAUDE.md updates throughout Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
149 lines
4.8 KiB
C++
149 lines
4.8 KiB
C++
#include "fake_video_source.h"
|
|
|
|
#include "api/video/video_frame.h"
|
|
#include "api/video/video_rotation.h"
|
|
#include "rtc_base/time_utils.h"
|
|
|
|
#include <cstring>
|
|
|
|
namespace {
|
|
|
|
constexpr int kWidth = 1280;
|
|
constexpr int kHeight = 720;
|
|
constexpr int kFps = 30;
|
|
constexpr uint8_t kBgY = 80; // dark background
|
|
constexpr uint8_t kDigitY = 235; // white digits
|
|
constexpr int kDigitW = 5;
|
|
constexpr int kDigitH = 7;
|
|
constexpr int kScale = 4;
|
|
constexpr int kDigitSpacing = 2; // pixels between digits (scaled)
|
|
constexpr int kMargin = 8; // top-left margin in pixels
|
|
|
|
// 5x7 bitmap font for digits 0-9. Each entry is 7 rows of 5-bit patterns.
|
|
// MSB = leftmost pixel.
|
|
static const uint8_t kDigitBitmaps[10][7] = {
|
|
// 0
|
|
{0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110},
|
|
// 1
|
|
{0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110},
|
|
// 2
|
|
{0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111},
|
|
// 3
|
|
{0b11111, 0b00010, 0b00100, 0b00010, 0b00001, 0b10001, 0b01110},
|
|
// 4
|
|
{0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010},
|
|
// 5
|
|
{0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110},
|
|
// 6
|
|
{0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110},
|
|
// 7
|
|
{0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000},
|
|
// 8
|
|
{0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110},
|
|
// 9
|
|
{0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100},
|
|
};
|
|
|
|
// 6 color tints cycling: red, green, blue, yellow, cyan, magenta
|
|
// UV values for each tint (in I420, U=Cb, V=Cr; neutral=128)
|
|
struct UVTint { uint8_t u; uint8_t v; };
|
|
static const UVTint kTints[6] = {
|
|
{90, 240}, // red
|
|
{54, 34}, // green
|
|
{240, 110}, // blue
|
|
{16, 146}, // yellow
|
|
{166, 16}, // cyan
|
|
{166, 240}, // magenta
|
|
};
|
|
|
|
} // namespace
|
|
|
|
FakeVideoTrackSource::FakeVideoTrackSource(int participantId)
|
|
: participantId_(participantId) {
|
|
const auto& tint = kTints[participantId % 6];
|
|
uTint_ = tint.u;
|
|
vTint_ = tint.v;
|
|
thread_ = std::thread(&FakeVideoTrackSource::GenerateThread, this);
|
|
}
|
|
|
|
FakeVideoTrackSource::~FakeVideoTrackSource() {
|
|
Stop();
|
|
}
|
|
|
|
rtc::scoped_refptr<FakeVideoTrackSource> FakeVideoTrackSource::Create(int participantId) {
|
|
return rtc::scoped_refptr<FakeVideoTrackSource>(
|
|
new rtc::RefCountedObject<FakeVideoTrackSource>(participantId));
|
|
}
|
|
|
|
void FakeVideoTrackSource::Stop() {
|
|
if (running_.exchange(false)) {
|
|
if (thread_.joinable()) {
|
|
thread_.join();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FakeVideoTrackSource::GenerateThread() {
|
|
int frameNumber = 0;
|
|
while (running_.load(std::memory_order_relaxed)) {
|
|
auto buffer = webrtc::I420Buffer::Create(kWidth, kHeight);
|
|
|
|
// Fill Y plane with dark background
|
|
memset(buffer->MutableDataY(), kBgY, buffer->StrideY() * kHeight);
|
|
|
|
// Fill U plane with tint
|
|
int uvHeight = (kHeight + 1) / 2;
|
|
memset(buffer->MutableDataU(), uTint_, buffer->StrideU() * uvHeight);
|
|
|
|
// Fill V plane with tint
|
|
memset(buffer->MutableDataV(), vTint_, buffer->StrideV() * uvHeight);
|
|
|
|
// Render frame counter digits
|
|
RenderDigits(buffer->MutableDataY(), buffer->StrideY(), frameNumber);
|
|
|
|
auto frame = webrtc::VideoFrame::Builder()
|
|
.set_video_frame_buffer(buffer)
|
|
.set_rotation(webrtc::kVideoRotation_0)
|
|
.set_timestamp_us(rtc::TimeMicros())
|
|
.build();
|
|
|
|
OnFrame(frame);
|
|
|
|
++frameNumber;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / kFps));
|
|
}
|
|
}
|
|
|
|
void FakeVideoTrackSource::RenderDigits(uint8_t* yPlane, int strideY, int frameNumber) {
|
|
// Convert frame number to decimal digits
|
|
char numStr[16];
|
|
snprintf(numStr, sizeof(numStr), "%d", frameNumber);
|
|
int numDigits = static_cast<int>(strlen(numStr));
|
|
|
|
int xOffset = kMargin;
|
|
for (int d = 0; d < numDigits; ++d) {
|
|
int digit = numStr[d] - '0';
|
|
const uint8_t* bitmap = kDigitBitmaps[digit];
|
|
|
|
for (int row = 0; row < kDigitH; ++row) {
|
|
uint8_t rowBits = bitmap[row];
|
|
for (int col = 0; col < kDigitW; ++col) {
|
|
if (rowBits & (1 << (kDigitW - 1 - col))) {
|
|
// Fill scaled pixel block
|
|
int px = xOffset + col * kScale;
|
|
int py = kMargin + row * kScale;
|
|
for (int sy = 0; sy < kScale; ++sy) {
|
|
for (int sx = 0; sx < kScale; ++sx) {
|
|
int x = px + sx;
|
|
int y = py + sy;
|
|
if (x < kWidth && y < kHeight) {
|
|
yPlane[y * strideY + x] = kDigitY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
xOffset += kDigitW * kScale + kDigitSpacing;
|
|
}
|
|
}
|