Prepare call video bitrate encoders without pausing capture
This commit is contained in:
1 parent
152d89f0dc
commit
0dec628e49
2 files changed
+38
-7
No files matched your search
@@ -28,6 +28,7 @@ bool CallVideoEncoder::open(const call_video_format& format, uint32_t bitrate, c
|
||||
void CallVideoEncoder::close() {
|
||||
if (m_encoder) DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call video encoder close generation=%llu pending_request=%llu",
|
||||
(unsigned long long)m_format.generation, (unsigned long long)m_request);
|
||||
m_prepared = {}; // Завершение фоновой подготовки ждём только при остановке/смене режима.
|
||||
m_encoder.reset(); m_config = {}; m_config.bitrate = 0; m_error.clear();
|
||||
m_format = {}; m_request = m_lastRequest = m_lastPts = m_lastKey = 0;
|
||||
m_pendingBitrate = 0; m_haveFrame = false;
|
||||
@@ -41,6 +42,22 @@ bool CallVideoEncoder::requestBitrate(uint64_t generation, uint64_t request, uin
|
||||
(unsigned long long)generation, (unsigned long long)m_format.generation, (unsigned long long)request,
|
||||
(unsigned long long)m_lastRequest, (unsigned long long)m_request, bitrate); return false;
|
||||
}
|
||||
if (bitrate != (uint32_t)m_config.bitrate) {
|
||||
LiveVideoConfig next = m_config; next.bitrate = bitrate;
|
||||
try {
|
||||
m_prepared = std::async(std::launch::async, [next] {
|
||||
Prepared result; QElapsedTimer timer; timer.start();
|
||||
try {
|
||||
result.encoder = std::make_unique<LiveVideoEncoder>();
|
||||
if (!result.encoder->open(next)) { result.error = result.encoder->error(); result.encoder.reset(); }
|
||||
} catch (const std::exception& e) { result.error = QString::fromUtf8(e.what()); result.encoder.reset(); }
|
||||
result.elapsed = timer.elapsed(); return result;
|
||||
});
|
||||
} catch (const std::exception& e) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call video prepare start failed request=%llu: %s", (unsigned long long)request, e.what());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
m_request = m_lastRequest = request; m_pendingBitrate = bitrate;
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call video bitrate queued generation=%llu request=%llu applied=%d target=%u last_key=%llu",
|
||||
(unsigned long long)generation, (unsigned long long)request, m_config.bitrate, bitrate, (unsigned long long)m_lastKey); return true;
|
||||
@@ -58,22 +75,28 @@ bool CallVideoEncoder::encode(const QImage& image, uint64_t pts, bool forceKey,
|
||||
}
|
||||
bool key = forceKey || !m_haveFrame || pts - m_lastKey >= 1000000;
|
||||
QList<LiveEncodedFrame> frames;
|
||||
if (key && m_request) {
|
||||
bool prepared = !m_prepared.valid() || m_prepared.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
|
||||
if (key && m_request && !prepared)
|
||||
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "call video preparation pending request=%llu; keep current encoder", (unsigned long long)m_request);
|
||||
if (key && m_request && prepared) {
|
||||
completion = {m_format.generation, m_request, 0};
|
||||
if (m_pendingBitrate == (uint32_t)m_config.bitrate) completion.bitrate = m_pendingBitrate;
|
||||
else {
|
||||
LiveVideoConfig next = m_config; next.bitrate = m_pendingBitrate;
|
||||
auto candidate = std::make_unique<LiveVideoEncoder>(); QElapsedTimer timer; timer.start();
|
||||
bool ready = candidate->open(next) && candidate->encode(image, pts, true, frames)
|
||||
&& frames.size() == 1 && frames.front().key && frames.front().pts == pts;
|
||||
auto replacement = m_prepared.get(); auto& candidate = replacement.encoder;
|
||||
QElapsedTimer timer; timer.start();
|
||||
bool ready = candidate && candidate->encode(image, pts, true, frames)
|
||||
&& frames.size() == 1 && frames.front().key && frames.front().pts == pts
|
||||
&& !call_video_check_key(&m_format, reinterpret_cast<const uint8_t*>(frames.front().data.constData()), frames.front().data.size());
|
||||
if (ready) {
|
||||
m_encoder = std::move(candidate); m_config = next; completion.bitrate = m_pendingBitrate;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL,
|
||||
"call video bitrate committed generation=%llu request=%llu bitrate=%u pts=%llu prepare_ms=%lld bytes=%lld",
|
||||
"call video bitrate committed generation=%llu request=%llu bitrate=%u pts=%llu background_ms=%lld commit_ms=%lld bytes=%lld",
|
||||
(unsigned long long)m_format.generation, (unsigned long long)m_request, completion.bitrate,
|
||||
(unsigned long long)pts, (long long)timer.elapsed(), (long long)frames.front().data.size());
|
||||
(unsigned long long)pts, (long long)replacement.elapsed, (long long)timer.elapsed(), (long long)frames.front().data.size());
|
||||
} else {
|
||||
m_error = candidate->error(); if (m_error.isEmpty()) m_error = "Replacement encoder did not produce an immediate IDR";
|
||||
m_error = candidate ? candidate->error() : replacement.error;
|
||||
if (m_error.isEmpty()) m_error = "Replacement encoder did not produce a compatible immediate IDR";
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_CALL, "call video replacement failed generation=%llu request=%llu target=%u: %s",
|
||||
(unsigned long long)m_format.generation, (unsigned long long)m_request, m_pendingBitrate, qPrintable(m_error));
|
||||
frames.clear();
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
#include "live_video_encoder.h"
|
||||
#include "../../../src/call/call_video.h"
|
||||
#include <future>
|
||||
|
||||
struct CallVideoRateCompletion {
|
||||
uint64_t generation = 0, request = 0;
|
||||
@@ -23,6 +24,11 @@ public:
|
||||
uint32_t bitrate() const { return m_encoder ? m_config.bitrate : 0; }
|
||||
QString error() const { return m_error; }
|
||||
private:
|
||||
struct Prepared {
|
||||
std::unique_ptr<LiveVideoEncoder> encoder;
|
||||
QString error;
|
||||
qint64 elapsed = 0;
|
||||
};
|
||||
std::unique_ptr<LiveVideoEncoder> m_encoder;
|
||||
LiveVideoConfig m_config;
|
||||
call_video_format m_format{};
|
||||
@@ -30,4 +36,6 @@ private:
|
||||
uint64_t m_request = 0, m_lastRequest = 0, m_lastPts = 0, m_lastKey = 0;
|
||||
uint32_t m_pendingBitrate = 0;
|
||||
bool m_haveFrame = false;
|
||||
// Отдельный контекст готовится в фоне; worker забирает его только после завершения open.
|
||||
std::future<Prepared> m_prepared;
|
||||
};
|
||||
Reference in new issue
Block a user