output/snapcast: implement SendTag()
This commit is contained in:
parent
6090bd2095
commit
abd416735d
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -60,6 +60,8 @@ public:
|
|||
|
||||
void LockClose() noexcept;
|
||||
|
||||
void SendStreamTags(ConstBuffer<void> payload) noexcept;
|
||||
|
||||
/**
|
||||
* Caller must lock the mutex.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue