diff --git a/server/Makefile b/server/Makefile index aeff9bbf..8433c5ef 100644 --- a/server/Makefile +++ b/server/Makefile @@ -10,7 +10,7 @@ else endif CXXFLAGS += -std=c++0x -Wall -Wno-unused-function -O3 -DASIO_STANDALONE -DVERSION=\"$(VERSION)\" -I. -I.. -I../externals/asio/asio/include -I../externals/popl/include -OBJ = snapServer.o config.o controlServer.o controlSession.o streamServer.o streamSession.o json/jsonrpc.o streamreader/streamUri.o streamreader/streamManager.o streamreader/pcmStream.o streamreader/pipeStream.o streamreader/fileStream.o streamreader/processStream.o streamreader/spotifyStream.o streamreader/watchdog.o encoder/encoderFactory.o encoder/flacEncoder.o encoder/pcmEncoder.o encoder/oggEncoder.o ../common/log.o ../common/sampleFormat.o ../message/pcmChunk.o +OBJ = snapServer.o config.o controlServer.o controlSession.o streamServer.o streamSession.o json/jsonrpc.o streamreader/streamUri.o streamreader/streamManager.o streamreader/pcmStream.o streamreader/pipeStream.o streamreader/fileStream.o streamreader/processStream.o streamreader/airplayStream.o streamreader/spotifyStream.o streamreader/watchdog.o encoder/encoderFactory.o encoder/flacEncoder.o encoder/pcmEncoder.o encoder/oggEncoder.o ../common/log.o ../common/sampleFormat.o ../message/pcmChunk.o ifeq ($(ENDIAN), BIG) CXXFLAGS += -DIS_BIG_ENDIAN diff --git a/server/streamreader/airplayStream.cpp b/server/streamreader/airplayStream.cpp new file mode 100644 index 00000000..003f7b8b --- /dev/null +++ b/server/streamreader/airplayStream.cpp @@ -0,0 +1,58 @@ +/*** + This file is part of snapcast + Copyright (C) 2014-2016 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 . +***/ + +#include "airplayStream.h" +#include "common/snapException.h" + + +using namespace std; + + + + +AirplayStream::AirplayStream(PcmListener* pcmListener, const StreamUri& uri) : ProcessStream(pcmListener, uri) +{ + logStderr_ = true; + + sampleFormat_ = SampleFormat("44100:16:2"); + uri_.query["sampleformat"] = sampleFormat_.getFormat(); + + string devicename = uri_.getQuery("devicename", "Snapcast"); + params_ = "--name=\"" + devicename + "\" --output=stdout"; +} + + +AirplayStream::~AirplayStream() +{ +} + + +void AirplayStream::initExeAndPath(const std::string& filename) +{ + exe_ = findExe(filename); + if (!fileExists(exe_) || (exe_ == "/")) + { + exe_ = findExe("shairport-sync"); + if (!fileExists(exe_)) + throw SnapException("shairport-sync not found"); + } + + if (exe_.find("/") != string::npos) + path_ = exe_.substr(0, exe_.find_last_of("/")); +} + diff --git a/server/streamreader/airplayStream.h b/server/streamreader/airplayStream.h new file mode 100644 index 00000000..ec53f747 --- /dev/null +++ b/server/streamreader/airplayStream.h @@ -0,0 +1,45 @@ +/*** + This file is part of snapcast + Copyright (C) 2014-2016 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 . +***/ + +#ifndef AIRPLAY_STREAM_H +#define AIRPLAY_STREAM_H + +#include "processStream.h" + +/// Starts shairport-sync and reads and PCM data from stdout +/** + * Starts librespot, reads PCM data from stdout, and passes the data to an encoder. + * Implements EncoderListener to get the encoded data. + * Data is passed to the PcmListener + * usage: + * snapserver -s "airplay:///shairport-sync?name=Airplay[&devicename=Snapcast]" + */ +class AirplayStream : public ProcessStream +{ +public: + /// ctor. Encoded PCM data is passed to the PipeListener + AirplayStream(PcmListener* pcmListener, const StreamUri& uri); + virtual ~AirplayStream(); + +protected: + virtual void initExeAndPath(const std::string& filename); + +}; + + +#endif diff --git a/server/streamreader/processStream.cpp b/server/streamreader/processStream.cpp index ca653544..9fb7cd37 100644 --- a/server/streamreader/processStream.cpp +++ b/server/streamreader/processStream.cpp @@ -118,8 +118,8 @@ void ProcessStream::onStderrMsg(const char* buffer, size_t n) if (logStderr_) { string line = trim_copy(string(buffer, n)); - if (line.find('\0') == string::npos) - logO << line << "\n"; + if ((line.find('\0') == string::npos) && !line.empty()) + logO << "(" << uri_.getQuery("name") << ") " << line << "\n"; } } @@ -200,7 +200,12 @@ void ProcessStream::worker() { logE << "Exception: " << e.what() << std::endl; process_->kill(); - chronos::sleep(30000); + int sleepMs = 30000; + while (active_ && (sleepMs > 0)) + { + chronos::sleep(100); + sleepMs -= 100; + } } } } diff --git a/server/streamreader/streamManager.cpp b/server/streamreader/streamManager.cpp index 1c1c8778..c161849b 100644 --- a/server/streamreader/streamManager.cpp +++ b/server/streamreader/streamManager.cpp @@ -17,6 +17,7 @@ ***/ #include "streamManager.h" +#include "airplayStream.h" #include "spotifyStream.h" #include "processStream.h" #include "pipeStream.h" @@ -74,6 +75,11 @@ PcmStream* StreamManager::addStream(const std::string& uri) streams_.push_back(make_shared(pcmListener_, streamUri)); return streams_.back().get(); } + else if (streamUri.scheme == "airplay") + { + streams_.push_back(make_shared(pcmListener_, streamUri)); + return streams_.back().get(); + } else { throw SnapException("Unknown stream type: " + streamUri.scheme);