Browse Source

Fallback desktop video on decoder rejection and verify receive-only codecs

master
evgeny 5 days ago
parent
commit
634ef222a4
  1. 33
      tools/chatgui/src/call_video_engine.cpp
  2. 5
      tools/chatgui/tests/test_call_video_encoder.cpp

33
tools/chatgui/src/call_video_engine.cpp

@ -146,6 +146,8 @@ void CallVideoEngine::transmit() {
CallVideoEncoder encoder; CallVideoEncoder encoder;
call_video_format format{}; call_video_format format{};
uint64_t appliedRevision = 0, keySerial = 0, request = 0, frameId = 0; uint64_t appliedRevision = 0, keySerial = 0, request = 0, frameId = 0;
uint64_t userRevision = 0, rejectedGeneration = 0;
unsigned excluded = 0;
int64_t lastPts = -1, lastKey = -1; int64_t lastPts = -1, lastKey = -1;
bool forceKey = true; bool forceKey = true;
while (m->running) { while (m->running) {
@ -161,11 +163,28 @@ void CallVideoEngine::transmit() {
raw = m->raw; raw = m->raw;
#endif #endif
} }
if (revision != userRevision) { userRevision = revision; excluded = 0; }
if (format.generation && state.decoded_generation == format.generation && !state.decoded_accepted
&& rejectedGeneration != format.generation) {
rejectedGeneration = format.generation; excluded |= 1u << format.codec;
if (!codec) { appliedRevision = 0; lastKey = -1; m->report(tr("Receiver rejected video; selecting fallback")); }
else {
m->report(tr("Receiver cannot decode selected codec"));
std::lock_guard<std::mutex> guard(m->lock); m->enabled = false; continue;
}
}
if (format.generation && state.have_peer && !call_video_format_supported(&caps, &state.peer, &format)) appliedRevision = 0;
if (!enabled || !state.have_peer || pts <= lastPts || (lastPts >= 0 && pts - lastPts < 33333)) { idle(); continue; } if (!enabled || !state.have_peer || pts <= lastPts || (lastPts >= 0 && pts - lastPts < 33333)) { idle(); continue; }
if (revision != appliedRevision) { if (revision != appliedRevision) {
if (lastKey >= 0 && pts - lastKey < 1000000) { idle(); continue; } if (lastKey >= 0 && pts - lastKey < 1000000) { idle(); continue; }
call_video_format next{format.generation + 1, (uint16_t)width, (uint16_t)height, 30, 0, (uint8_t)codec, 0, 4000000, 0}; call_video_format next{format.generation + 1, (uint16_t)width, (uint16_t)height, 30, 0, (uint8_t)codec, 0, 4000000, 0};
if (call_video_choose(&caps, &state.peer, &next) <= 0 || !encoder.open(next, 1000000)) { bool opened = false;
for (int candidate = 2; candidate >= 1; --candidate) {
if ((codec && codec != candidate) || (excluded & (1u << candidate))) continue;
next.codec = candidate; next.level = next.profile = 0;
if (call_video_choose(&caps, &state.peer, &next) > 0 && encoder.open(next, 1000000)) { opened = true; break; }
}
if (!opened) {
m->report(tr("Selected video mode is unavailable to the peer")); m->report(tr("Selected video mode is unavailable to the peer"));
std::lock_guard<std::mutex> guard(m->lock); if (m->revision == revision) m->enabled = false; std::lock_guard<std::mutex> guard(m->lock); if (m->revision == revision) m->enabled = false;
continue; continue;
@ -209,6 +228,7 @@ void CallVideoEngine::receive() {
std::deque<Picture> pictures; std::deque<Picture> pictures;
CallVideoDecoder decoder; CallVideoDecoder decoder;
uint64_t sourceEpoch = 0; int64_t lastShown = -1, report = 0; unsigned shown = 0; uint64_t sourceEpoch = 0; int64_t lastShown = -1, report = 0; unsigned shown = 0;
uint64_t reportedGeneration = 0, rejectedGeneration = 0;
while (m->running) { while (m->running) {
int64_t now = nowUs(); call_clock_snapshot clock{}; int64_t now = nowUs(); call_clock_snapshot clock{};
call_audio_get_clock(m->id, now, &clock); call_audio_get_clock(m->id, now, &clock);
@ -228,9 +248,16 @@ void CallVideoEngine::receive() {
if (pictures.size() < 16) { if (pictures.size() < 16) {
call_video_frame packet{}; call_video_frame packet{};
if (call_video_port_pull(m->id, &packet) == 1) { if (call_video_port_pull(m->id, &packet) == 1) {
if (packet.format.generation == rejectedGeneration) { u_free((void*)packet.data); continue; }
QImage image; QImage image;
if (decoder.decode(packet, image)) pictures.push_back({packet.pts_us, image}); if (decoder.decode(packet, image)) {
else call_video_port_request_key(m->id); pictures.push_back({packet.pts_us, image});
if (reportedGeneration != packet.format.generation) {
reportedGeneration = packet.format.generation; call_video_port_decoded(m->id, reportedGeneration, 1);
}
} else {
rejectedGeneration = packet.format.generation; call_video_port_decoded(m->id, rejectedGeneration, 0);
}
u_free((void*)packet.data); continue; u_free((void*)packet.data); continue;
} }
} }

5
tools/chatgui/tests/test_call_video_encoder.cpp

@ -45,6 +45,11 @@ private slots:
void switchesAtScheduledIdr_data() { void switchesAtScheduledIdr_data() {
QTest::addColumn<int>("codec"); QTest::newRow("H264") << 1; QTest::newRow("H265") << 2; QTest::addColumn<int>("codec"); QTest::newRow("H264") << 1; QTest::newRow("H265") << 2;
} }
void independentDecoders() {
auto caps = CallVideoDecoder::capabilities();
QCOMPARE(caps.decode[0].profiles, uint8_t(3)); QCOMPARE(caps.decode[1].profiles, uint8_t(1));
QCOMPARE(caps.encode[0].width, uint16_t(0)); QCOMPARE(caps.encode[1].width, uint16_t(0));
}
void switchesAtScheduledIdr() { void switchesAtScheduledIdr() {
QFETCH(int, codec); QFETCH(int, codec);
auto config = configuration(codec); auto config = configuration(codec);

Loading…
Cancel
Save