Codebase list snapcast / 11ef762
Update upstream source from tag 'upstream/0.18.1' Update to upstream version '0.18.1' with Debian dir ac1c94bb09e970a4452cda584e3f522beccb75f2 Felix Geyer 4 years ago
15 changed file(s) with 46 addition(s) and 65 deletion(s). Raw diff Collapse all Expand all
00 cmake_minimum_required(VERSION 3.2)
11
2 project(snapcast LANGUAGES CXX VERSION 0.18.0)
2 project(snapcast LANGUAGES CXX VERSION 0.18.1)
33 set(PROJECT_DESCRIPTION "Multi-room client-server audio player")
44 set(PROJECT_URL "https://github.com/badaix/snapcast")
55
00 # Snapcast changelog
11
2 ## Version 0.18.1
3
4 ### Bugfixes
5
6 - Fix random server crash or deadlock during stream client disconnect
7 - Fix random server crash or deadlock during control client disconnect
8 - Fix airplay stream buffer allocation (PR #536)
9
10 _Johannes Pohl <snapcast@badaix.de> Tue, 28 Jan 2020 00:13:37 +0200_
11
212 ## Version 0.18.0
313
414 ### Features
515
616 - Add TCP stream reader
17 - Configurable number of server worker threads
718
819 ### Bugfixes
920
1728
1829 - Refactored stream readers
1930 - Server can run on a single thread
20 - Configurable number of server worker threads
2131
2232 _Johannes Pohl <snapcast@badaix.de> Wed, 22 Jan 2020 00:13:37 +0200_
2333
1313 # You should have received a copy of the GNU General Public License
1414 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
16 VERSION = 0.18.0
16 VERSION = 0.18.1
1717 BIN = snapclient
1818
1919 ifeq ($(TARGET), FREEBSD)
1313 # You should have received a copy of the GNU General Public License
1414 # along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
16 VERSION = 0.18.0
16 VERSION = 0.18.1
1717 BIN = snapserver
1818
1919 ifeq ($(TARGET), FREEBSD)
2525 #include <boost/asio.hpp>
2626 #include <condition_variable>
2727 #include <memory>
28 #include <mutex>
2928 #include <set>
3029 #include <string>
3130
114114
115115 void ControlSessionHttp::start()
116116 {
117 auto self = shared_from_this();
118 http::async_read(socket_, buffer_, req_,
119 boost::asio::bind_executor(strand_, [this, self](boost::system::error_code ec, std::size_t bytes) { on_read(ec, bytes); }));
117 http::async_read(socket_, buffer_, req_, boost::asio::bind_executor(strand_, [ this, self = shared_from_this() ](
118 boost::system::error_code ec, std::size_t bytes) { on_read(ec, bytes); }));
120119 }
121120
122121
255254 // Create a WebSocket session by transferring the socket
256255 // std::make_shared<websocket_session>(std::move(socket_), state_)->run(std::move(req_));
257256 ws_ = make_unique<websocket::stream<beast::tcp_stream>>(std::move(socket_));
258 auto self = shared_from_this();
259 ws_->async_accept(req_, [this, self](beast::error_code ec) { on_accept_ws(ec); });
257 ws_->async_accept(req_, [ this, self = shared_from_this() ](beast::error_code ec) { on_accept_ws(ec); });
260258 LOG(DEBUG) << "websocket upgrade\n";
261259 return;
262260 }
270268 auto sp = std::make_shared<response_type>(std::forward<decltype(response)>(response));
271269
272270 // Write the response
273 auto self = this->shared_from_this();
274 http::async_write(this->socket_, *sp, boost::asio::bind_executor(strand_, [this, self, sp](beast::error_code ec, std::size_t bytes) {
271 http::async_write(this->socket_, *sp,
272 boost::asio::bind_executor(strand_, [ this, self = this->shared_from_this(), sp ](beast::error_code ec, std::size_t bytes) {
275273 this->on_write(ec, bytes, sp->need_eof());
276274 }));
277275 });
314312 if (!ws_)
315313 return;
316314
317 strand_.post([this, message]() {
315 strand_.post([ this, self = shared_from_this(), message ]() {
318316 messages_.emplace_back(message);
319317 if (messages_.size() > 1)
320318 {
330328 if (!ws_)
331329 return;
332330
333 auto self(shared_from_this());
334331 auto message = messages_.front();
335 ws_->async_write(boost::asio::buffer(message), boost::asio::bind_executor(strand_, [this, self](std::error_code ec, std::size_t length) {
332 ws_->async_write(boost::asio::buffer(message),
333 boost::asio::bind_executor(strand_, [ this, self = shared_from_this() ](std::error_code ec, std::size_t length) {
336334 messages_.pop_front();
337335 if (ec)
338336 {
373371 void ControlSessionHttp::do_read_ws()
374372 {
375373 // Read a message into our buffer
376 auto self(shared_from_this());
377 ws_->async_read(
378 buffer_, boost::asio::bind_executor(strand_, [this, self](beast::error_code ec, std::size_t bytes_transferred) { on_read_ws(ec, bytes_transferred); }));
374 ws_->async_read(buffer_, boost::asio::bind_executor(strand_, [ this, self = shared_from_this() ](beast::error_code ec, std::size_t bytes_transferred) {
375 on_read_ws(ec, bytes_transferred);
376 }));
379377 }
380378
381379
4040 void ControlSessionTcp::do_read()
4141 {
4242 const std::string delimiter = "\n";
43 auto self(shared_from_this());
4443 boost::asio::async_read_until(
45 socket_, streambuf_, delimiter, boost::asio::bind_executor(strand_, [this, self, delimiter](const std::error_code& ec, std::size_t bytes_transferred) {
44 socket_, streambuf_, delimiter,
45 boost::asio::bind_executor(strand_, [ this, self = shared_from_this(), delimiter ](const std::error_code& ec, std::size_t bytes_transferred) {
4646 if (ec)
4747 {
4848 LOG(ERROR) << "Error while reading from control socket: " << ec.message() << "\n";
9191
9292 void ControlSessionTcp::sendAsync(const std::string& message)
9393 {
94 strand_.post([this, message]() {
94 strand_.post([ this, self = shared_from_this(), message ]() {
9595 messages_.emplace_back(message);
9696 if (messages_.size() > 1)
9797 {
104104
105105 void ControlSessionTcp::send_next()
106106 {
107 auto self(shared_from_this());
108107 auto message = messages_.front();
109108 boost::asio::async_write(socket_, boost::asio::buffer(message + "\r\n"),
110 boost::asio::bind_executor(strand_, [this, self](std::error_code ec, std::size_t length) {
109 boost::asio::bind_executor(strand_, [ this, self = shared_from_this() ](std::error_code ec, std::size_t length) {
111110 messages_.pop_front();
112111 if (ec)
113112 {
165165 // and encode the buffer content in the next iteration
166166 void OpusEncoder::encode(const msg::PcmChunk* chunk)
167167 {
168 LOG(DEBUG) << "encode " << chunk->duration<std::chrono::milliseconds>().count() << "ms\n";
168 // LOG(TRACE) << "encode " << chunk->duration<std::chrono::milliseconds>().count() << "ms\n";
169169 uint32_t offset = 0;
170170
171171 // check if there is something left from the last call to encode and fill the remainder buffer to
174174 {
175175 offset = std::min(static_cast<uint32_t>(remainder_max_size_ - remainder_->payloadSize), chunk->payloadSize);
176176 memcpy(remainder_->payload + remainder_->payloadSize, chunk->payload, offset);
177 LOG(DEBUG) << "remainder buffer size: " << remainder_->payloadSize << "/" << remainder_max_size_ << ", appending " << offset << " bytes\n";
177 // LOG(TRACE) << "remainder buffer size: " << remainder_->payloadSize << "/" << remainder_max_size_ << ", appending " << offset << " bytes\n";
178178 remainder_->payloadSize += offset;
179179
180180 if (remainder_->payloadSize < remainder_max_size_)
195195 uint32_t bytes = ms2bytes(duration);
196196 while (chunk->payloadSize - offset >= bytes)
197197 {
198 LOG(DEBUG) << "encoding " << duration << "ms (" << bytes << "), offset: " << offset << ", chunk size: " << chunk->payloadSize - offset << "\n";
198 // LOG(TRACE) << "encoding " << duration << "ms (" << bytes << "), offset: " << offset << ", chunk size: " << chunk->payloadSize - offset << "\n";
199199 encode(chunk->format, chunk->payload + offset, bytes);
200200 offset += bytes;
201201 }
221221 encoded_.resize(size);
222222
223223 opus_int32 len = opus_encode(enc_, (opus_int16*)data, samples_per_channel, encoded_.data(), size);
224 LOG(DEBUG) << "Encode " << samples_per_channel << " frames, size " << size << " bytes, encoded: " << len << " bytes" << '\n';
224 // LOG(TRACE) << "Encode " << samples_per_channel << " frames, size " << size << " bytes, encoded: " << len << " bytes" << '\n';
225225
226226 if (len > 0)
227227 {
4444
4545 void StreamSession::read_next()
4646 {
47 shared_ptr<StreamSession> self;
48 try
49 {
50 self = shared_from_this();
51 }
52 catch (const std::bad_weak_ptr& e)
53 {
54 LOG(ERROR, LOG_TAG) << "read_next: Error getting shared from this\n";
55 return;
56 }
57
5847 boost::asio::async_read(socket_, boost::asio::buffer(buffer_, base_msg_size_),
59 boost::asio::bind_executor(strand_, [this, self](boost::system::error_code ec, std::size_t length) mutable {
48 boost::asio::bind_executor(strand_, [ this, self = shared_from_this() ](boost::system::error_code ec, std::size_t length) mutable {
6049 if (ec)
6150 {
6251 LOG(ERROR, LOG_TAG) << "Error reading message header of length " << length << ": " << ec.message() << "\n";
138127
139128 void StreamSession::send_next()
140129 {
141 shared_ptr<StreamSession> self;
142 try
143 {
144 self = shared_from_this();
145 }
146 catch (const std::bad_weak_ptr& e)
147 {
148 LOG(ERROR, LOG_TAG) << "send_next: Error getting shared from this\n";
149 return;
150 }
151
152130 auto buffer = messages_.front();
153
154 boost::asio::async_write(socket_, buffer, boost::asio::bind_executor(strand_, [this, self, buffer](boost::system::error_code ec, std::size_t length) {
131 boost::asio::async_write(socket_, buffer,
132 boost::asio::bind_executor(strand_, [ this, self = shared_from_this(), buffer ](boost::system::error_code ec, std::size_t length) {
155133 messages_.pop_front();
156134 if (ec)
157135 {
167145
168146 void StreamSession::sendAsync(shared_const_buffer const_buf, bool send_now)
169147 {
170 strand_.post([this, const_buf, send_now]() {
148 strand_.post([ this, self = shared_from_this(), const_buf, send_now ]() {
171149 if (send_now)
172150 messages_.push_front(const_buf);
173151 else
2626 #include <condition_variable>
2727 #include <deque>
2828 #include <memory>
29 #include <mutex>
3029 #include <set>
3130 #include <sstream>
3231 #include <string>
5858
5959 // XXX: Check if pipe exists, delete or throw error
6060
61 sampleFormat_ = SampleFormat("44100:16:2");
62 uri_.query["sampleformat"] = sampleFormat_.getFormat();
63
6461 port_ = cpt::stoul(uri_.getQuery("port", "5000"));
6562
6663 string devicename = uri_.getQuery("devicename", "Snapcast");
102102 {
103103 uint64_t last_read = bytes_read_;
104104 wait(state_timer_, std::chrono::milliseconds(500 + chunk_ms_), [this, last_read] {
105 LOG(DEBUG, "AsioStream") << "check state last: " << last_read << ", read: " << bytes_read_ << "\n";
105 LOG(TRACE, "AsioStream") << "check state last: " << last_read << ", read: " << bytes_read_ << "\n";
106106 if (bytes_read_ != last_read)
107107 setState(ReaderState::kPlaying);
108108 else
3333
3434 LibrespotStream::LibrespotStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) : ProcessStream(pcmListener, ioc, uri)
3535 {
36 sampleFormat_ = SampleFormat("44100:16:2");
37 uri_.query["sampleformat"] = sampleFormat_.getFormat();
38 // chunk is created in PcmStream ctor, using the (possibly wrongly) configured sample format
39 // we have to recreate it using spotify's native sample format
40 chunk_ = std::make_unique<msg::PcmChunk>(sampleFormat_, chunk_ms_);
41
4236 wd_timeout_sec_ = cpt::stoul(uri_.getQuery("wd_timeout", "7800")); ///< 130min
4337
4438 string username = uri_.getQuery("username", "");
2828 #include <boost/asio/io_context.hpp>
2929 #include <condition_variable>
3030 #include <map>
31 #include <mutex>
3231 #include <string>
3332
3433
7474 }
7575 else if ((streamUri.scheme == "spotify") || (streamUri.scheme == "librespot"))
7676 {
77 // Overwrite sample format here instead of inside the constructor, to make sure
78 // that all constructors of all parent classes also use the overwritten sample
79 // format.
80 streamUri.query[kUriSampleFormat] = "44100:16:2";
7781 stream = make_shared<LibrespotStream>(pcmListener_, ioc_, streamUri);
7882 }
7983 else if (streamUri.scheme == "airplay")
8084 {
85 // Overwrite sample format here instead of inside the constructor, to make sure
86 // that all constructors of all parent classes also use the overwritten sample
87 // format.
88 streamUri.query[kUriSampleFormat] = "44100:16:2";
8189 stream = make_shared<AirplayStream>(pcmListener_, ioc_, streamUri);
8290 }
8391 else if (streamUri.scheme == "tcp")