Added basics for maintaining metadata by stream on server and pushing to clients.

Modified Spotify stream handler to get the track name from Libreelec's stderr.
Note, to support artist/album (or album art) we need to modify Libreelec
to print these.
This commit is contained in:
frafall 2017-11-20 20:44:54 +01:00
parent 034c7f5f98
commit d444052233
11 changed files with 166 additions and 2 deletions

View file

@ -133,6 +133,15 @@ void Controller::onMessageReceived(ClientConnection* connection, const msg::Base
player_->setMute(serverSettings_->isMuted()); player_->setMute(serverSettings_->isMuted());
player_->start(); player_->start();
} }
else if (baseMessage.type == message_type::kStreamTags)
{
streamTags_.reset(new msg::StreamTags());
streamTags_->deserialize(baseMessage, buffer);
LOG(INFO) << "Tag received: artist = " << streamTags_->getArtist() << "\n";
LOG(INFO) << "Tag received: album = " << streamTags_->getAlbum() << "\n";
LOG(INFO) << "Tag received: track = " << streamTags_->getTrack() << "\n";
}
if (baseMessage.type != message_type::kTime) if (baseMessage.type != message_type::kTime)
if (sendTimeSyncMessage(1000)) if (sendTimeSyncMessage(1000))

View file

@ -24,6 +24,7 @@
#include "decoder/decoder.h" #include "decoder/decoder.h"
#include "message/message.h" #include "message/message.h"
#include "message/serverSettings.h" #include "message/serverSettings.h"
#include "message/streamTags.h"
#include "player/pcmDevice.h" #include "player/pcmDevice.h"
#ifdef HAS_ALSA #ifdef HAS_ALSA
#include "player/alsaPlayer.h" #include "player/alsaPlayer.h"
@ -73,6 +74,7 @@ private:
std::unique_ptr<Decoder> decoder_; std::unique_ptr<Decoder> decoder_;
std::unique_ptr<Player> player_; std::unique_ptr<Player> player_;
std::shared_ptr<msg::ServerSettings> serverSettings_; std::shared_ptr<msg::ServerSettings> serverSettings_;
std::shared_ptr<msg::StreamTags> streamTags_;
std::shared_ptr<msg::CodecHeader> headerChunk_; std::shared_ptr<msg::CodecHeader> headerChunk_;
std::mutex receiveMutex_; std::mutex receiveMutex_;

0
common/metatags.cpp Normal file
View file

35
common/metatags.h Normal file
View file

@ -0,0 +1,35 @@
/***
This file is part of snapcast
Copyright (C) 2014-2017 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/>.
***/
#ifndef METATAGS_DATA_H
#define METATAGS_DATA_H
#include "message/streamTags.h"
class MetaTags
{
public:
MetaTags();
virtual ~MetaTags();
private:
};
#endif

View file

@ -57,9 +57,10 @@ enum message_type
kServerSettings = 3, kServerSettings = 3,
kTime = 4, kTime = 4,
kHello = 5, kHello = 5,
kStreamTags = 6,
kFirst = kBase, kFirst = kBase,
kLast = kHello kLast = kStreamTags
}; };

78
message/streamTags.h Normal file
View file

@ -0,0 +1,78 @@
/***
This file is part of snapcast
Copyright (C) 2014-2017 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/>.
***/
#ifndef STREAMTAGS_H
#define STREAMTAGS_H
#include "jsonMessage.h"
namespace msg
{
class StreamTags : public JsonMessage
{
public:
StreamTags() : JsonMessage(message_type::kStreamTags)
{
msg["meta_artist"] = "";
msg["meta_album"] = "";
msg["meta_track"] = "";
}
virtual ~StreamTags()
{
}
std::string getArtist() const
{
return msg["meta_artist"];
}
std::string getAlbum() const
{
return msg["meta_album"];
}
std::string getTrack() const
{
return msg["meta_track"];
}
void setArtist(std::string artist)
{
msg["meta_artist"] = artist;
}
void setAlbum(std::string album)
{
msg["meta_album"] = album;
}
void setTrack(std::string track)
{
msg["meta_track"] = track;
}
};
}
#endif

View file

@ -33,7 +33,7 @@ endif
CXXFLAGS += $(ADD_CFLAGS) -std=c++0x -Wall -Wno-unused-function -O3 -DASIO_STANDALONE -DVERSION=\"$(VERSION)\" -I. -I.. -isystem ../externals/asio/asio/include -I../externals/popl/include -I../externals/aixlog/include -I../externals/jsonrpcpp/lib -I../externals CXXFLAGS += $(ADD_CFLAGS) -std=c++0x -Wall -Wno-unused-function -O3 -DASIO_STANDALONE -DVERSION=\"$(VERSION)\" -I. -I.. -isystem ../externals/asio/asio/include -I../externals/popl/include -I../externals/aixlog/include -I../externals/jsonrpcpp/lib -I../externals
LDFLAGS = -lvorbis -lvorbisenc -logg -lFLAC LDFLAGS = -lvorbis -lvorbisenc -logg -lFLAC
OBJ = snapServer.o config.o controlServer.o controlSession.o streamServer.o streamSession.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/sampleFormat.o ../message/pcmChunk.o ../externals/jsonrpcpp/lib/jsonrp.o OBJ = snapServer.o config.o controlServer.o controlSession.o streamServer.o streamSession.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/metatags.o ../common/sampleFormat.o ../message/pcmChunk.o ../externals/jsonrpcpp/lib/jsonrp.o
ifneq (,$(TARGET)) ifneq (,$(TARGET))

View file

@ -19,6 +19,7 @@
#include "streamServer.h" #include "streamServer.h"
#include "message/time.h" #include "message/time.h"
#include "message/hello.h" #include "message/hello.h"
#include "message/streamTags.h"
#include "aixlog.hpp" #include "aixlog.hpp"
#include "config.h" #include "config.h"
#include <iostream> #include <iostream>
@ -516,6 +517,14 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM
Config::instance().save(); Config::instance().save();
// Send the group stream tags
LOG(INFO) << "request kStreamTags\n";
//auto metaTags = make_shared<msg::StreamTags>();
//metaTags->setArtist(stream->getMeta()->getArtist());
//connection->sendAsync(metaTags);
connection->sendAsync(stream->getMeta());
LOG(INFO) << "kStreamTags sent\n";
connection->setPcmStream(stream); connection->setPcmStream(stream);
auto headerChunk = stream->getHeader(); auto headerChunk = stream->getHeader();
connection->sendAsync(headerChunk); connection->sendAsync(headerChunk);

View file

@ -55,6 +55,9 @@ PcmStream::PcmStream(PcmListener* pcmListener, const StreamUri& uri) :
dryoutMs_ = cpt::stoul(uri_.query["dryout_ms"]); dryoutMs_ = cpt::stoul(uri_.query["dryout_ms"]);
else else
dryoutMs_ = 2000; dryoutMs_ = 2000;
// meta_.reset(new msg::StreamTags);
meta_ = make_shared<msg::StreamTags>();
} }

View file

@ -30,6 +30,7 @@
#include "externals/json.hpp" #include "externals/json.hpp"
#include "common/sampleFormat.h" #include "common/sampleFormat.h"
#include "message/codecHeader.h" #include "message/codecHeader.h"
#include "message/streamTags.h"
class PcmStream; class PcmStream;
@ -84,6 +85,12 @@ public:
virtual ReaderState getState() const; virtual ReaderState getState() const;
virtual json toJson() const; virtual json toJson() const;
//const msg::StreamTags *getMeta()
std::shared_ptr<msg::StreamTags> getMeta()
{
return meta_;
}
protected: protected:
std::condition_variable cv_; std::condition_variable cv_;
@ -104,6 +111,9 @@ protected:
std::unique_ptr<Encoder> encoder_; std::unique_ptr<Encoder> encoder_;
std::string name_; std::string name_;
ReaderState state_; ReaderState state_;
// Stream metadata
std::shared_ptr<msg::StreamTags> meta_;
}; };

View file

@ -16,6 +16,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
***/ ***/
#include <regex>
#include "spotifyStream.h" #include "spotifyStream.h"
#include "common/snapException.h" #include "common/snapException.h"
#include "common/utils/string_utils.h" #include "common/utils/string_utils.h"
@ -92,6 +93,9 @@ void SpotifyStream::initExeAndPath(const std::string& filename)
void SpotifyStream::onStderrMsg(const char* buffer, size_t n) void SpotifyStream::onStderrMsg(const char* buffer, size_t n)
{ {
// Watch stderr for 'Loading track' messages and set the stream metadata
// For more than track name check: https://github.com/plietar/librespot/issues/154
/// Watch will kill librespot if there was no message received for 130min /// Watch will kill librespot if there was no message received for 130min
// 2016-11-02 22-05-15 [out] TRACE:librespot::stream: allocated stream 3580 // 2016-11-02 22-05-15 [out] TRACE:librespot::stream: allocated stream 3580
// 2016-11-02 22-05-15 [Debug] DEBUG:librespot::audio_file2: Got channel 3580 // 2016-11-02 22-05-15 [Debug] DEBUG:librespot::audio_file2: Got channel 3580
@ -118,6 +122,19 @@ void SpotifyStream::onStderrMsg(const char* buffer, size_t n)
{ {
LOG(INFO) << "(" << getName() << ") " << logmsg << "\n"; LOG(INFO) << "(" << getName() << ") " << logmsg << "\n";
} }
// Check for metadata
if (logmsg.find("Loading track") != string::npos)
{
regex re("Loading track \"(.*)\"");
smatch m;
if (regex_search(logmsg, m, re))
{
LOG(INFO) << "Loading track (" << m[1] << ")\n";
getMeta()->setTrack(m[1]);
}
}
} }