Embed stream players and verify grid and concurrent playback
This commit is contained in:
1 parent
1b49c4023b
commit
05deb15c91
3 files changed
+77
-5
No files matched your search
@@ -50,6 +50,7 @@ struct StreamPlayerWindow::Playback {
|
||||
} audioGap;
|
||||
uint64_t id = ++nextId, group = 0, source = 0, epoch = 0;
|
||||
std::atomic<bool> running{true}, audioAvailable{false};
|
||||
std::atomic<bool> complete{false}, failed{false};
|
||||
std::atomic<float> volume{1.0f};
|
||||
float outputVolume = 0.0f;
|
||||
uint64_t audioGeneration = 0;
|
||||
@@ -86,11 +87,12 @@ struct StreamPlayerWindow::Playback {
|
||||
}
|
||||
};
|
||||
|
||||
StreamPlayerWindow::StreamPlayerWindow(uint64_t group, uint64_t source, uint64_t epoch, const QString& name, QWidget* parent)
|
||||
StreamPlayerWindow::StreamPlayerWindow(uint64_t group, uint64_t source, uint64_t epoch, const QString& name, QWidget* parent, bool embedded)
|
||||
: QWidget(parent), m_playback(std::make_unique<Playback>()) {
|
||||
m_playback->group = group; m_playback->source = source; m_playback->epoch = epoch;
|
||||
setWindowTitle(text("Live stream: %1").arg(name)); resize(800, 500);
|
||||
auto* layout = new QVBoxLayout(this); layout->addWidget(new QLabel(name, this));
|
||||
setObjectName(QStringLiteral("streamPlayer_%1").arg(source));
|
||||
auto* layout = new QVBoxLayout(this); auto* title = new QLabel(name, this); layout->addWidget(title);
|
||||
m_status = new QLabel(text("Connecting…"), this); layout->addWidget(m_status);
|
||||
m_render = new VideoRenderWidget(this, true); layout->addWidget(m_render, 1);
|
||||
auto* controls = new QHBoxLayout;
|
||||
@@ -98,6 +100,11 @@ StreamPlayerWindow::StreamPlayerWindow(uint64_t group, uint64_t source, uint64_t
|
||||
volume->setAccessibleName(text("Stream volume")); controls->addWidget(new QLabel(text("Volume"), this)); controls->addWidget(volume);
|
||||
auto* fullscreen = new QPushButton(text("Fullscreen"), this); controls->addWidget(fullscreen);
|
||||
auto* close = new QPushButton(text("Close player"), this); controls->addWidget(close); layout->addLayout(controls);
|
||||
if (embedded) {
|
||||
fullscreen->hide(); close->hide(); layout->setContentsMargins(3, 3, 3, 3); layout->setSpacing(2);
|
||||
title->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); title->setToolTip(name);
|
||||
m_status->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred); setMinimumSize(0, 0);
|
||||
}
|
||||
connect(volume, &QSlider::valueChanged, this, [this](int value) { m_playback->volume = value / 100.0f; });
|
||||
connect(fullscreen, &QPushButton::clicked, this, [this] { if (isFullScreen()) showNormal(); else showFullScreen(); });
|
||||
connect(close, &QPushButton::clicked, this, &QWidget::close);
|
||||
@@ -109,6 +116,13 @@ StreamPlayerWindow::StreamPlayerWindow(uint64_t group, uint64_t source, uint64_t
|
||||
|
||||
StreamPlayerWindow::~StreamPlayerWindow() { stop(); }
|
||||
|
||||
bool StreamPlayerWindow::finished() const { return m_playback->complete; }
|
||||
bool StreamPlayerWindow::failed() const { return m_playback->failed; }
|
||||
int StreamPlayerWindow::drainTimeoutMs() const {
|
||||
auto& p = *m_playback; std::lock_guard<std::mutex> guard(p.lock);
|
||||
return p.complete ? 0 : static_cast<int>(std::clamp<int64_t>((p.ready - p.clock(nowUs())) / 1000 + 2000, 2000, 30000));
|
||||
}
|
||||
|
||||
/* Hardware callback заполняет только ближайшую порцию; source time тишины остаётся неподвижным. */
|
||||
void StreamPlayerWindow::attachAudio() {
|
||||
if (!m_playback->running || m_audioAttached) return;
|
||||
@@ -173,8 +187,9 @@ void StreamPlayerWindow::tick() {
|
||||
(unsigned long long)p.id, image.width(), image.height(), (long long)image.bytesPerLine());
|
||||
emit framePresented();
|
||||
}
|
||||
if (finished) {
|
||||
m_status->setText(text("Stream ended"));
|
||||
if (finished || p.failed) {
|
||||
if (finished) m_status->setText(text("Stream ended"));
|
||||
p.complete = true;
|
||||
SoundManager::instance()->detachStream(p.id); m_audioAttached = false; m_timer.stop();
|
||||
}
|
||||
}
|
||||
@@ -200,6 +215,7 @@ void StreamPlayerWindow::decode() {
|
||||
auto& p = *m_playback;
|
||||
#ifndef HAVE_FFMPEG
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop live decoder unavailable: FFmpeg disabled");
|
||||
p.failed = p.complete = true;
|
||||
emit statusChanged(text("Live video playback requires FFmpeg"));
|
||||
#else
|
||||
AVCodecContext* decoder = nullptr; AVFrame* frame = av_frame_alloc(); AVPacket* packet = av_packet_alloc();
|
||||
@@ -227,6 +243,7 @@ void StreamPlayerWindow::decode() {
|
||||
deadline = nowUs() + 10000000; emit statusChanged(text("Waiting for a key frame…"));
|
||||
};
|
||||
auto fail = [&](const QString& reason) {
|
||||
p.failed = p.complete = true;
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_STREAM, "desktop viewer failed id=%llu: %s", (unsigned long long)p.id, qPrintable(reason));
|
||||
emit statusChanged(reason);
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user