|
|
|
|
@ -31,6 +31,7 @@ struct CallVideoEngine::Impl {
|
|
|
|
|
bool enabled = false; |
|
|
|
|
int width = 640, height = 480, codec = 0; |
|
|
|
|
int64_t pts = -1, backend = -1, anchor = -1; |
|
|
|
|
uint64_t captured = 0; |
|
|
|
|
QImage shown; |
|
|
|
|
uint64_t shownSerial = 0, deliveredSerial = 0; |
|
|
|
|
int64_t previewPts = -1, previewAt = 0; |
|
|
|
|
@ -138,7 +139,7 @@ void CallVideoEngine::setVideo(bool enabled, const QByteArray& cameraId, int wid
|
|
|
|
|
} |
|
|
|
|
pts = m->anchor + frame.startTime() - m->backend; |
|
|
|
|
} |
|
|
|
|
if (pts > m->pts) { m->raw = frame; m->pts = pts; } |
|
|
|
|
if (pts > m->pts) { m->raw = frame; m->pts = pts; ++m->captured; } |
|
|
|
|
}, Qt::DirectConnection); |
|
|
|
|
connect(m->camera.get(), &QCamera::errorOccurred, this, [this](QCamera::Error, const QString& error) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call camera failed: %s", qPrintable(error)); m->report(error); |
|
|
|
|
@ -170,20 +171,33 @@ void CallVideoEngine::transmit() {
|
|
|
|
|
unsigned excluded = 0; |
|
|
|
|
int64_t lastPts = -1, lastKey = -1; |
|
|
|
|
bool forceKey = true; |
|
|
|
|
int64_t reportAt = nowUs(), encodeMax = 0; |
|
|
|
|
uint64_t captureBase = 0; |
|
|
|
|
unsigned encodedCount = 0, keyCount = 0, sendBusy = 0, paced = 0; |
|
|
|
|
int64_t pacedPts = -1; |
|
|
|
|
while (m->running) { |
|
|
|
|
struct call_video_port_status state{}; |
|
|
|
|
if (call_video_port_status(m->id, &state) < 0) break; |
|
|
|
|
if (state.error) { m->report(tr("Core rejected video format")); break; } |
|
|
|
|
uint64_t revision; bool enabled; int width, height, codec; int64_t pts; |
|
|
|
|
uint64_t revision, captured; bool enabled; int width, height, codec; int64_t pts; |
|
|
|
|
#ifdef HAVE_STREAM_CAPTURE |
|
|
|
|
QVideoFrame raw; |
|
|
|
|
#endif |
|
|
|
|
{ std::lock_guard<std::mutex> guard(m->lock); |
|
|
|
|
revision = m->revision; enabled = m->enabled; width = m->width; height = m->height; codec = m->codec; pts = m->pts; |
|
|
|
|
captured = m->captured; |
|
|
|
|
#ifdef HAVE_STREAM_CAPTURE |
|
|
|
|
raw = m->raw; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
int64_t now = nowUs(); |
|
|
|
|
if (now - reportAt >= 1000000) { |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, |
|
|
|
|
"video capture flow call=%llu interval_us=%lld captured=%llu encoded=%u keys=%u send_busy=%u paced=%u encode_max_us=%lld", |
|
|
|
|
(unsigned long long)m->id, (long long)(now - reportAt), (unsigned long long)(captured - captureBase), |
|
|
|
|
encodedCount, keyCount, sendBusy, paced, (long long)encodeMax); |
|
|
|
|
reportAt = now; captureBase = captured; encodedCount = keyCount = sendBusy = paced = 0; encodeMax = 0; |
|
|
|
|
} |
|
|
|
|
if (revision != userRevision) { userRevision = revision; excluded = 0; } |
|
|
|
|
if (format.generation && state.decoded_generation == format.generation && !state.decoded_accepted |
|
|
|
|
&& rejectedGeneration != format.generation) { |
|
|
|
|
@ -195,7 +209,11 @@ void CallVideoEngine::transmit() {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
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) { idle(); continue; } |
|
|
|
|
if (lastPts >= 0 && pts - lastPts < 33333) { |
|
|
|
|
if (pacedPts != pts) { pacedPts = pts; ++paced; } |
|
|
|
|
idle(); continue; |
|
|
|
|
} |
|
|
|
|
if (revision != appliedRevision) { |
|
|
|
|
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}; |
|
|
|
|
@ -228,19 +246,23 @@ void CallVideoEngine::transmit() {
|
|
|
|
|
QImage image = raw.toImage(); |
|
|
|
|
if (image.isNull()) { DEBUG_WARN(DEBUG_CATEGORY_CALL, "camera frame conversion failed"); lastPts = pts; continue; } |
|
|
|
|
LiveEncodedFrame output; CallVideoRateCompletion completion; |
|
|
|
|
int64_t encodeStarted = nowUs(); |
|
|
|
|
bool encoded = encoder.encode(image, pts, forceKey, output, completion); lastPts = pts; |
|
|
|
|
encodeMax = std::max(encodeMax, nowUs() - encodeStarted); |
|
|
|
|
if (completion.request) { |
|
|
|
|
call_video_port_applied(m->id, completion.generation, completion.request, completion.bitrate); |
|
|
|
|
m->report(QString("%1 · %2×%3 · %4 kbit/s").arg(format.codec == 2 ? "H.265" : "H.264") |
|
|
|
|
.arg(format.width).arg(format.height).arg(encoder.bitrate() / 1000)); |
|
|
|
|
} |
|
|
|
|
if (!encoded) { forceKey = true; m->report(encoder.error()); continue; } |
|
|
|
|
++encodedCount; if (output.key) ++keyCount; |
|
|
|
|
if (output.key) lastKey = pts; |
|
|
|
|
call_video_frame packet{}; |
|
|
|
|
packet.call_id = m->id; packet.frame_id = ++frameId; packet.pts_us = pts; packet.format = format; |
|
|
|
|
packet.size = packet.length = output.data.size(); packet.data = reinterpret_cast<const uint8_t*>(output.data.constData()); |
|
|
|
|
packet.flags = output.key ? CALL_VIDEO_KEY : 0; |
|
|
|
|
forceKey = call_video_port_send(&packet) != 1; |
|
|
|
|
if (forceKey) ++sendBusy; |
|
|
|
|
#else |
|
|
|
|
idle(); |
|
|
|
|
#endif |
|
|
|
|
|