output/snapcast: implement SendTag()
This commit is contained in:
@@ -119,7 +119,9 @@ if get_option('snapcast')
|
|||||||
'snapcast/SnapcastOutputPlugin.cxx',
|
'snapcast/SnapcastOutputPlugin.cxx',
|
||||||
'snapcast/Client.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
|
# TODO: the Snapcast plugin needs just the "wave" encoder, but this
|
||||||
# enables all available encoders
|
# enables all available encoders
|
||||||
|
|||||||
@@ -236,6 +236,27 @@ SnapcastClient::SendWireChunk(ConstBuffer<void> payload,
|
|||||||
return ::SendWireChunk(GetSocket(), next_id++, payload, t);
|
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
|
BufferedSocket::InputResult
|
||||||
SnapcastClient::OnSocketInput(void *data, size_t length) noexcept
|
SnapcastClient::OnSocketInput(void *data, size_t length) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,6 +60,8 @@ public:
|
|||||||
|
|
||||||
void LockClose() noexcept;
|
void LockClose() noexcept;
|
||||||
|
|
||||||
|
void SendStreamTags(ConstBuffer<void> payload) noexcept;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Caller must lock the mutex.
|
* Caller must lock the mutex.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public:
|
|||||||
|
|
||||||
std::chrono::steady_clock::duration Delay() const noexcept override;
|
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;
|
size_t Play(const void *chunk, size_t size) override;
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "Internal.hxx"
|
#include "Internal.hxx"
|
||||||
#include "Client.hxx"
|
#include "Client.hxx"
|
||||||
#include "output/OutputAPI.hxx"
|
#include "output/OutputAPI.hxx"
|
||||||
|
#include "output/Features.h"
|
||||||
#include "encoder/EncoderInterface.hxx"
|
#include "encoder/EncoderInterface.hxx"
|
||||||
#include "encoder/Configured.hxx"
|
#include "encoder/Configured.hxx"
|
||||||
#include "encoder/plugins/WaveEncoderPlugin.hxx"
|
#include "encoder/plugins/WaveEncoderPlugin.hxx"
|
||||||
@@ -31,6 +32,10 @@
|
|||||||
#include "util/DeleteDisposer.hxx"
|
#include "util/DeleteDisposer.hxx"
|
||||||
#include "config/Net.hxx"
|
#include "config/Net.hxx"
|
||||||
|
|
||||||
|
#ifdef HAVE_YAJL
|
||||||
|
#include "lib/yajl/Gen.hxx"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -207,6 +212,79 @@ SnapcastOutput::Delay() const noexcept
|
|||||||
: std::chrono::steady_clock::duration::zero();
|
: 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
|
size_t
|
||||||
SnapcastOutput::Play(const void *chunk, size_t size)
|
SnapcastOutput::Play(const void *chunk, size_t size)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user