output/snapcast: implement SendTag()

This commit is contained in:
Max Kellermann 2021-02-22 23:02:38 +01:00
parent 6090bd2095
commit abd416735d
5 changed files with 105 additions and 2 deletions

View File

@ -119,7 +119,9 @@ if get_option('snapcast')
'snapcast/SnapcastOutputPlugin.cxx',
'snapcast/Client.cxx',
]
output_plugins_deps += [ event_dep, net_dep ]
output_plugins_deps += [ event_dep, net_dep, yajl_dep ]
output_features.set('HAVE_YAJL', yajl_dep.found())
# TODO: the Snapcast plugin needs just the "wave" encoder, but this
# enables all available encoders

View File

@ -236,6 +236,27 @@ SnapcastClient::SendWireChunk(ConstBuffer<void> payload,
return ::SendWireChunk(GetSocket(), next_id++, payload, t);
}
static bool
SendStreamTags(SocketDescriptor s, const PackedBE16 id,
const ConstBuffer<void> payload) noexcept
{
const PackedLE32 payload_size = payload.size;
SnapcastBase base{};
base.type = uint16_t(SnapcastMessageType::STREAM_TAGS);
base.id = id;
base.sent = ToSnapcastTimestamp(std::chrono::steady_clock::now());
base.size = sizeof(payload_size) + payload.size;
return SendT(s, base) && SendT(s, payload_size) && Send(s, payload);
}
void
SnapcastClient::SendStreamTags(ConstBuffer<void> payload) noexcept
{
::SendStreamTags(GetSocket(), next_id++, payload);
}
BufferedSocket::InputResult
SnapcastClient::OnSocketInput(void *data, size_t length) noexcept
{

View File

@ -60,6 +60,8 @@ public:
void LockClose() noexcept;
void SendStreamTags(ConstBuffer<void> payload) noexcept;
/**
* Caller must lock the mutex.
*/

View File

@ -165,7 +165,7 @@ public:
std::chrono::steady_clock::duration Delay() const noexcept override;
// TODO: void SendTag(const Tag &tag) override;
void SendTag(const Tag &tag) override;
size_t Play(const void *chunk, size_t size) override;

View File

@ -21,6 +21,7 @@
#include "Internal.hxx"
#include "Client.hxx"
#include "output/OutputAPI.hxx"
#include "output/Features.h"
#include "encoder/EncoderInterface.hxx"
#include "encoder/Configured.hxx"
#include "encoder/plugins/WaveEncoderPlugin.hxx"
@ -31,6 +32,10 @@
#include "util/DeleteDisposer.hxx"
#include "config/Net.hxx"
#ifdef HAVE_YAJL
#include "lib/yajl/Gen.hxx"
#endif
#include <cassert>
#include <string.h>
@ -207,6 +212,79 @@ SnapcastOutput::Delay() const noexcept
: std::chrono::steady_clock::duration::zero();
}
#ifdef HAVE_YAJL
static constexpr struct {
TagType type;
const char *name;
} snapcast_tags[] = {
/* these tags are mentioned in an example in
snapcast/common/message/stream_tags.hpp */
{ TAG_ARTIST, "artist" },
{ TAG_ALBUM, "album" },
{ TAG_TITLE, "track" },
{ TAG_MUSICBRAINZ_TRACKID, "musicbrainzid" },
};
static bool
TranslateTagType(Yajl::Gen &gen, const Tag &tag, TagType type,
const char *name) noexcept
{
// TODO: support multiple values?
const char *value = tag.GetValue(type);
if (value == nullptr)
return false;
gen.String(name);
gen.String(value);
return true;
}
static std::string
ToJson(const Tag &tag) noexcept
{
Yajl::Gen gen(nullptr);
gen.OpenMap();
bool empty = true;
for (const auto [type, name] : snapcast_tags)
if (TranslateTagType(gen, tag, type, name))
empty = false;
if (empty)
return {};
gen.CloseMap();
const auto result = gen.GetBuffer();
return {(const char *)result.data, result.size};
}
#endif
void
SnapcastOutput::SendTag(const Tag &tag)
{
#ifdef HAVE_YAJL
if (!LockHasClients())
return;
const auto json = ToJson(tag);
if (json.empty())
return;
const ConstBuffer payload(json.data(), json.size());
const std::lock_guard<Mutex> protect(mutex);
// TODO: enqueue StreamTags, don't send directly
for (auto &client : clients)
client.SendStreamTags(payload.ToVoid());
#else
(void)tag;
#endif
}
size_t
SnapcastOutput::Play(const void *chunk, size_t size)
{