|
|
|
|
@ -33,6 +33,8 @@ struct CallVideoEngine::Impl {
|
|
|
|
|
int64_t pts = -1, backend = -1, anchor = -1; |
|
|
|
|
QImage shown; |
|
|
|
|
uint64_t shownSerial = 0, deliveredSerial = 0; |
|
|
|
|
int64_t previewPts = -1, previewAt = 0; |
|
|
|
|
bool previewVisible = false; |
|
|
|
|
QString status; |
|
|
|
|
bool statusDirty = false; |
|
|
|
|
void report(const QString& text) { |
|
|
|
|
@ -55,6 +57,23 @@ CallVideoEngine::CallVideoEngine(QObject* parent) : QObject(parent), m(std::make
|
|
|
|
|
if (m->statusDirty) { status = m->status; m->statusDirty = false; report = true; } } |
|
|
|
|
if (changed) emit imageChanged(image); |
|
|
|
|
if (report) emit statusChanged(status); |
|
|
|
|
#ifdef HAVE_STREAM_CAPTURE |
|
|
|
|
QVideoFrame preview; bool clear = false; |
|
|
|
|
int64_t now = nowUs(); |
|
|
|
|
{ std::lock_guard<std::mutex> guard(m->lock); |
|
|
|
|
if (!m->enabled && m->previewVisible) { m->previewVisible = false; clear = true; } |
|
|
|
|
if (m->enabled && m->raw.isValid() && m->pts != m->previewPts && now - m->previewAt >= 100000) { |
|
|
|
|
preview = m->raw; m->previewPts = m->pts; m->previewAt = now; |
|
|
|
|
} } |
|
|
|
|
if (clear) emit localImageChanged({}); |
|
|
|
|
if (preview.isValid()) { |
|
|
|
|
QImage local = preview.toImage(); |
|
|
|
|
if (!local.isNull()) { |
|
|
|
|
m->previewVisible = true; |
|
|
|
|
emit localImageChanged(local.scaled(320, 240, Qt::KeepAspectRatio, Qt::SmoothTransformation)); |
|
|
|
|
} else DEBUG_WARN(DEBUG_CATEGORY_CALL, "call camera preview conversion failed"); |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
CallVideoEngine::~CallVideoEngine() { stop(); } |
|
|
|
|
@ -76,8 +95,9 @@ void CallVideoEngine::closeCamera() {
|
|
|
|
|
if (m->sink) disconnect(m->sink.get(), nullptr, this, nullptr); |
|
|
|
|
if (m->camera) { disconnect(m->camera.get(), nullptr, this, nullptr); m->camera->stop(); } |
|
|
|
|
m->capture.reset(); m->camera.reset(); m->sink.reset(); |
|
|
|
|
std::lock_guard<std::mutex> guard(m->lock); m->raw = {}; m->pts = m->backend = m->anchor = -1; |
|
|
|
|
{ std::lock_guard<std::mutex> guard(m->lock); m->raw = {}; m->pts = m->backend = m->anchor = -1; } |
|
|
|
|
#endif |
|
|
|
|
m->previewPts = -1; m->previewVisible = false; emit localImageChanged({}); |
|
|
|
|
} |
|
|
|
|
void CallVideoEngine::stop() { |
|
|
|
|
m->running = false; m_timer.stop(); closeCamera(); |
|
|
|
|
@ -231,7 +251,7 @@ void CallVideoEngine::receive() {
|
|
|
|
|
struct Picture { int64_t pts; QImage image; }; |
|
|
|
|
std::deque<Picture> pictures; |
|
|
|
|
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, lastPictureAt = 0; unsigned shown = 0; |
|
|
|
|
uint64_t reportedGeneration = 0, rejectedGeneration = 0; |
|
|
|
|
while (m->running) { |
|
|
|
|
int64_t now = nowUs(); call_clock_snapshot clock{}; |
|
|
|
|
@ -249,7 +269,14 @@ void CallVideoEngine::receive() {
|
|
|
|
|
} |
|
|
|
|
pictures.pop_front(); |
|
|
|
|
} |
|
|
|
|
if (!selected.isNull()) { std::lock_guard<std::mutex> guard(m->lock); m->shown = selected; ++m->shownSerial; ++shown; } |
|
|
|
|
if (!selected.isNull()) { |
|
|
|
|
std::lock_guard<std::mutex> guard(m->lock); m->shown = selected; ++m->shownSerial; ++shown; lastPictureAt = now; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// VIDEO_OFF пока отсутствует в протоколе: убираем застывшую картинку после прекращения показа.
|
|
|
|
|
if (lastPictureAt && now - lastPictureAt >= 5000000) { |
|
|
|
|
std::lock_guard<std::mutex> guard(m->lock); m->shown = {}; ++m->shownSerial; lastPictureAt = 0; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "video playback hidden: no playable frame for 5s"); |
|
|
|
|
} |
|
|
|
|
if (pictures.size() < 16) { |
|
|
|
|
call_video_frame packet{}; |
|
|
|
|
|