Preserve stream capture settings and refine audio playback
This commit is contained in:
1 parent
198ebc0acb
commit
d60f8e4b1b
16 files changed
+244
-43
No files matched your search
@@ -40,6 +40,13 @@ QString text(const char* value) { return QCoreApplication::translate("ChatGui",
|
||||
|
||||
struct StreamPlayerWindow::Playback {
|
||||
struct Video { QImage image; int64_t pts; };
|
||||
struct AudioGap {
|
||||
uint64_t count = 0, silentFrames = 0;
|
||||
unsigned requested = 0, received = 0;
|
||||
int64_t reserve = 0, feedAge = -1, workerAge = -1, audioPts = -1;
|
||||
size_t videoQueue = 0;
|
||||
bool held = false;
|
||||
} audioGap;
|
||||
uint64_t id = ++nextId, group = 0, source = 0, epoch = 0;
|
||||
std::atomic<bool> running{true}, audioAvailable{false};
|
||||
std::atomic<float> volume{1.0f};
|
||||
@@ -56,6 +63,7 @@ struct StreamPlayerWindow::Playback {
|
||||
uint64_t audioFrameStart = 0, shown = 0, underruns = 0, endFrames = UINT64_MAX;
|
||||
bool started = false, presenting = false, videoReady = false, ended = false, videoEnded = false;
|
||||
bool activeAudio = false, holdAudio = false, resync = false;
|
||||
int64_t audioFedAt = 0, audioFedPts = -1, workerAt = 0;
|
||||
~Playback() { stream_playback_audio_destroy(sound); }
|
||||
|
||||
uint64_t played(int64_t now) const {
|
||||
@@ -128,7 +136,13 @@ void StreamPlayerWindow::openAudio() {
|
||||
if (count < 0) { p.resync = true; count = 0; }
|
||||
if ((unsigned)count < frames) {
|
||||
stream_playback_audio_silence(p.sound, frames - (unsigned)count);
|
||||
if (p.presenting && !p.ended) p.underruns++;
|
||||
if (p.presenting && !p.ended) {
|
||||
p.underruns++; p.audioGap.count++; p.audioGap.silentFrames += frames - (unsigned)count;
|
||||
p.audioGap.requested = frames; p.audioGap.received = (unsigned)count; p.audioGap.held = p.holdAudio;
|
||||
p.audioGap.reserve = p.ready - p.position; p.audioGap.videoQueue = p.videos.size();
|
||||
p.audioGap.feedAge = p.audioFedAt ? now - p.audioFedAt : -1; p.audioGap.audioPts = p.audioFedPts;
|
||||
p.audioGap.workerAge = p.workerAt ? now - p.workerAt : -1;
|
||||
}
|
||||
}
|
||||
float volume = p.volume.load();
|
||||
for (int i = 0; i < count * 2; i++) pcm[i] = static_cast<int16_t>(pcm[i] * volume);
|
||||
@@ -200,6 +214,8 @@ void StreamPlayerWindow::decode() {
|
||||
uint32_t revision = 0;
|
||||
uint64_t decoded = 0, discarded = 0, reportedShown = 0;
|
||||
int64_t deadline = nowUs() + 10000000, report = nowUs(), decoderProgress = -1, lastInputAt = -1;
|
||||
uint64_t reportedGaps = 0;
|
||||
int64_t gapReportAt = 0;
|
||||
auto reset = [&](bool request) {
|
||||
avcodec_free_context(&decoder); av_packet_unref(packet); waitKey = true; finishing = eosSent = false;
|
||||
decoderProgress = lastInputAt = -1;
|
||||
@@ -210,6 +226,7 @@ void StreamPlayerWindow::decode() {
|
||||
p.decoderLead = 0; p.framePeriod = 33333; p.lastInputPts = p.lastDecodedPts = -1;
|
||||
p.ended = p.videoEnded = false;
|
||||
p.audioDacAt = 0; p.audioFrameStart = 0; p.holdAudio = p.resync = false; p.endFrames = UINT64_MAX;
|
||||
p.audioFedAt = 0; p.audioFedPts = -1;
|
||||
}
|
||||
if (request && !gui_stream_resync(p.id)) DEBUG_WARN(DEBUG_CATEGORY_STREAM, "desktop playback resync refused");
|
||||
deadline = nowUs() + 10000000; emit statusChanged(text("Waiting for a key frame…"));
|
||||
@@ -226,7 +243,7 @@ void StreamPlayerWindow::decode() {
|
||||
if (!status.initialized) return;
|
||||
if (!p.started) {
|
||||
p.activeAudio = status.audio_enabled && p.audioAvailable;
|
||||
if (p.activeAudio) p.sound = stream_playback_audio_create(status.start_us);
|
||||
if (p.activeAudio) p.sound = stream_playback_audio_create(status.start_us, 40);
|
||||
if (p.activeAudio && !p.sound) { p.running = false; return; }
|
||||
p.position = status.start_us; p.clockAt = nowUs(); p.started = true;
|
||||
stream_playback_clock_reset(&p.adaptation, p.id);
|
||||
@@ -283,7 +300,18 @@ void StreamPlayerWindow::decode() {
|
||||
else if (!gui_stream_open(p.id, p.group, p.source, p.epoch)) fail(text("Could not connect to the stream"));
|
||||
else while (p.running) {
|
||||
bool resync;
|
||||
{ std::lock_guard<std::mutex> guard(p.lock); resync = p.resync; }
|
||||
Playback::AudioGap gap;
|
||||
{ std::lock_guard<std::mutex> guard(p.lock); resync = p.resync; p.workerAt = nowUs(); gap = p.audioGap; }
|
||||
/* Callback сохраняет снимок; печать выполняется worker без блокировки аудиоустройства. */
|
||||
if (gap.count != reportedGaps && nowUs() - gapReportAt >= 1000000) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_DEBUG,
|
||||
"desktop audio gap id=%llu count=%llu delta=%llu silence_frames=%llu cause=%s got=%u/%u "
|
||||
"reserve_us=%lld feed_age_us=%lld worker_age_us=%lld audio_pts=%lld videoQ=%zu",
|
||||
(unsigned long long)p.id, (unsigned long long)gap.count, (unsigned long long)(gap.count - reportedGaps),
|
||||
(unsigned long long)gap.silentFrames, gap.held ? "video_hold" : "pcm_short", gap.received, gap.requested,
|
||||
(long long)gap.reserve, (long long)gap.feedAge, (long long)gap.workerAge, (long long)gap.audioPts, gap.videoQueue);
|
||||
reportedGaps = gap.count; gapReportAt = nowUs();
|
||||
}
|
||||
if (resync) reset(true);
|
||||
observe();
|
||||
drainVideo();
|
||||
@@ -359,10 +387,15 @@ void StreamPlayerWindow::decode() {
|
||||
observe();
|
||||
if (record.pts > INT64_MAX) { fail(text("Invalid stream timestamp")); break; }
|
||||
if (record.kind == StreamRecordKind::Audio) {
|
||||
int result = 0;
|
||||
int result = 0, wanted = 0, stillWanted = 0;
|
||||
{ std::lock_guard<std::mutex> guard(p.lock);
|
||||
if (p.sound) wanted = stream_playback_audio_needs_input(p.sound);
|
||||
if (p.sound) result = stream_playback_audio_feed(p.sound, (int64_t)record.pts,
|
||||
reinterpret_cast<const uint8_t*>(record.data.constData()), (size_t)record.data.size()); }
|
||||
reinterpret_cast<const uint8_t*>(record.data.constData()), (size_t)record.data.size());
|
||||
if (p.sound && result == 0) { p.audioFedAt = nowUs(); p.audioFedPts = (int64_t)record.pts; }
|
||||
if (p.sound) stillWanted = stream_playback_audio_needs_input(p.sound); }
|
||||
DEBUG_TRACE(DEBUG_CATEGORY_DEBUG, "desktop audio feed id=%llu pts=%llu result=%d needs_input=%d->%d",
|
||||
(unsigned long long)p.id, (unsigned long long)record.pts, result, wanted, stillWanted);
|
||||
if (result == -2) reset(true);
|
||||
else if (result < 0) { fail(text("Stream audio decoder failed")); break; }
|
||||
continue;
|
||||
|
||||
Reference in new issue
Block a user