mirror of
https://github.com/badaix/snapcast.git
synced 2025-05-19 03:56:14 +02:00
Add meta stream source
This commit is contained in:
parent
f1e672d375
commit
27a9e710a2
20 changed files with 367 additions and 64 deletions
170
server/streamreader/meta_stream.cpp
Normal file
170
server/streamreader/meta_stream.cpp
Normal file
|
@ -0,0 +1,170 @@
|
|||
/***
|
||||
This file is part of snapcast
|
||||
Copyright (C) 2014-2020 Johannes Pohl
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <memory>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "common/aixlog.hpp"
|
||||
#include "common/snap_exception.hpp"
|
||||
#include "common/utils/string_utils.hpp"
|
||||
#include "encoder/encoder_factory.hpp"
|
||||
#include "meta_stream.hpp"
|
||||
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace streamreader
|
||||
{
|
||||
|
||||
static constexpr auto LOG_TAG = "MetaStream";
|
||||
|
||||
|
||||
MetaStream::MetaStream(PcmListener* pcmListener, std::vector<std::shared_ptr<PcmStream>> streams, boost::asio::io_context& ioc, const StreamUri& uri)
|
||||
: PcmStream(pcmListener, ioc, uri), first_read_(true)
|
||||
{
|
||||
auto path_components = utils::string::split(uri.path, '/');
|
||||
for (const auto& component : path_components)
|
||||
{
|
||||
if (component.empty())
|
||||
continue;
|
||||
LOG(INFO, LOG_TAG) << "Stream: " << component << "\n";
|
||||
bool found = false;
|
||||
for (const auto stream : streams)
|
||||
{
|
||||
if (stream->getName() == component)
|
||||
{
|
||||
streams_.push_back(stream);
|
||||
stream->addListener(this);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
throw SnapException("Unknown stream: \"" + component + "\"");
|
||||
}
|
||||
|
||||
for (const auto stream : streams_)
|
||||
LOG(INFO, LOG_TAG) << "Stream: " << stream->getName() << ", " << stream->getUri().toString() << "\n";
|
||||
|
||||
if (!streams_.empty())
|
||||
{
|
||||
active_stream_ = streams_.front();
|
||||
resampler_ = make_unique<Resampler>(active_stream_->getSampleFormat(), sampleFormat_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MetaStream::~MetaStream()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::start()
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "Start, sampleformat: " << sampleFormat_.toString() << "\n";
|
||||
PcmStream::start();
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::stop()
|
||||
{
|
||||
active_ = false;
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::onMetaChanged(const PcmStream* pcmStream)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "onMetaChanged: " << pcmStream->getName() << "\n";
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (pcmStream != active_stream_.get())
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::onStateChanged(const PcmStream* pcmStream, ReaderState state)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "onStateChanged: " << pcmStream->getName() << ", state: " << static_cast<int>(state) << "\n";
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (const auto stream : streams_)
|
||||
{
|
||||
if (stream->getState() == ReaderState::kPlaying)
|
||||
{
|
||||
if ((state_ != ReaderState::kPlaying) || (active_stream_ != stream))
|
||||
first_read_ = true;
|
||||
|
||||
if (active_stream_ != stream)
|
||||
{
|
||||
active_stream_ = stream;
|
||||
resampler_ = make_unique<Resampler>(active_stream_->getSampleFormat(), sampleFormat_);
|
||||
}
|
||||
|
||||
setState(ReaderState::kPlaying);
|
||||
return;
|
||||
}
|
||||
}
|
||||
active_stream_ = nullptr;
|
||||
setState(ReaderState::kIdle);
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::onChunkRead(const PcmStream* pcmStream, const msg::PcmChunk& chunk)
|
||||
{
|
||||
// LOG(TRACE, LOG_TAG) << "onChunkRead: " << pcmStream->getName() << ", duration: " << chunk.durationMs() << "\n";
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (pcmStream != active_stream_.get())
|
||||
return;
|
||||
// active_stream_->sampleFormat_
|
||||
// sampleFormat_
|
||||
|
||||
if (first_read_)
|
||||
{
|
||||
first_read_ = false;
|
||||
LOG(INFO, LOG_TAG) << "first read, updating timestamp\n";
|
||||
tvEncodedChunk_ = std::chrono::steady_clock::now() - chunk.duration<std::chrono::microseconds>();
|
||||
}
|
||||
|
||||
auto resampled_chunk = resampler_->resample(std::make_shared<msg::PcmChunk>(chunk));
|
||||
if (resampled_chunk)
|
||||
chunkRead(*resampled_chunk);
|
||||
// chunkRead(chunk);
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::onChunkEncoded(const PcmStream* pcmStream, std::shared_ptr<msg::PcmChunk> chunk, double duration)
|
||||
{
|
||||
std::ignore = pcmStream;
|
||||
std::ignore = chunk;
|
||||
std::ignore = duration;
|
||||
// LOG(TRACE, LOG_TAG) << "onChunkEncoded: " << pcmStream->getName() << ", duration: " << duration << "\n";
|
||||
// chunkEncoded(*encoder_, chunk, duration);
|
||||
}
|
||||
|
||||
|
||||
void MetaStream::onResync(const PcmStream* pcmStream, double ms)
|
||||
{
|
||||
LOG(DEBUG, LOG_TAG) << "onResync: " << pcmStream->getName() << ", duration: " << ms << " ms\n";
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (pcmStream != active_stream_.get())
|
||||
return;
|
||||
resync(std::chrono::nanoseconds(static_cast<int64_t>(ms * 1000000)));
|
||||
}
|
||||
|
||||
|
||||
} // namespace streamreader
|
Loading…
Add table
Add a link
Reference in a new issue