summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2020-09-21 13:01:51 +0200
committerMax Kellermann <max@musicpd.org>2020-09-21 14:53:18 +0200
commit85849c93964161c15cc0cec7e68bb4917ee93bcd (patch)
tree86da2f7d606d26521012b5668e96975ea511b679
parentd3c257d97d4ff7671f7761745fd3c5fd23fecc5f (diff)
decoder/plugin: add method protocols()
Similar to commit 4e2a551f30c1e1db13933d15c44d9186a2f37959 but for decoder plugins. This is tailored for the FFmpeg decoder plugin which implements some protocols (e.g. RTSP) as demuxer plugin.
-rw-r--r--src/CommandLine.cxx4
-rw-r--r--src/decoder/DecoderPlugin.cxx14
-rw-r--r--src/decoder/DecoderPlugin.hxx30
-rw-r--r--src/decoder/Thread.cxx57
-rw-r--r--src/ls.cxx16
-rw-r--r--test/run_decoder.cxx7
6 files changed, 126 insertions, 2 deletions
diff --git a/src/CommandLine.cxx b/src/CommandLine.cxx
index 1413d5374..96d5be4b1 100644
--- a/src/CommandLine.cxx
+++ b/src/CommandLine.cxx
@@ -154,6 +154,10 @@ static void version()
for (; *suffixes != nullptr; ++suffixes)
printf(" %s", *suffixes);
+ if (plugin.protocols != nullptr)
+ for (const auto &i : plugin.protocols())
+ printf(" %s", i.c_str());
+
printf("\n");
});
diff --git a/src/decoder/DecoderPlugin.cxx b/src/decoder/DecoderPlugin.cxx
index d7e6de357..bbb3966fc 100644
--- a/src/decoder/DecoderPlugin.cxx
+++ b/src/decoder/DecoderPlugin.cxx
@@ -18,11 +18,25 @@
*/
#include "DecoderPlugin.hxx"
+#include "util/StringCompare.hxx"
#include "util/StringUtil.hxx"
+#include <algorithm>
#include <cassert>
bool
+DecoderPlugin::SupportsUri(const char *uri) const noexcept
+{
+ if (protocols != nullptr) {
+ const auto p = protocols();
+ return std::any_of(p.begin(), p.end(), [uri](const auto &schema)
+ { return StringStartsWithIgnoreCase(uri, schema.c_str()); } );
+ }
+
+ return false;
+}
+
+bool
DecoderPlugin::SupportsSuffix(const char *suffix) const noexcept
{
#if !CLANG_CHECK_VERSION(3,6)
diff --git a/src/decoder/DecoderPlugin.hxx b/src/decoder/DecoderPlugin.hxx
index da4409e47..c09aa5032 100644
--- a/src/decoder/DecoderPlugin.hxx
+++ b/src/decoder/DecoderPlugin.hxx
@@ -23,6 +23,8 @@
#include "util/Compiler.h"
#include <forward_list> // IWYU pragma: export
+#include <set>
+#include <string>
struct ConfigBlock;
class InputStream;
@@ -51,6 +53,16 @@ struct DecoderPlugin {
void (*finish)() noexcept = nullptr;
/**
+ * Return a set of supported protocols.
+ */
+ std::set<std::string> (*protocols)() noexcept = nullptr;
+
+ /**
+ * Decode an URI with a protocol listed in protocols().
+ */
+ void (*uri_decode)(DecoderClient &client, const char *uri) = nullptr;
+
+ /**
* Decode a stream (data read from an #InputStream object).
*
* Either implement this method or file_decode(). If
@@ -143,6 +155,14 @@ struct DecoderPlugin {
return copy;
}
+ constexpr auto WithProtocols(std::set<std::string> (*_protocols)() noexcept,
+ void (*_uri_decode)(DecoderClient &client, const char *uri)) noexcept {
+ auto copy = *this;
+ copy.protocols = _protocols;
+ copy.uri_decode = _uri_decode;
+ return copy;
+ }
+
constexpr auto WithSuffixes(const char *const*_suffixes) noexcept {
auto copy = *this;
copy.suffixes = _suffixes;
@@ -184,6 +204,13 @@ struct DecoderPlugin {
}
/**
+ * Decode an URI which is supported (check SupportsUri()).
+ */
+ void UriDecode(DecoderClient &client, const char *uri) const {
+ uri_decode(client, uri);
+ }
+
+ /**
* Decode a file.
*/
template<typename P>
@@ -218,6 +245,9 @@ struct DecoderPlugin {
return container_scan(path, tnum);
}
+ gcc_pure
+ bool SupportsUri(const char *uri) const noexcept;
+
/**
* Does the plugin announce the specified file name suffix?
*/
diff --git a/src/decoder/Thread.cxx b/src/decoder/Thread.cxx
index 9167cd421..d4d85edb4 100644
--- a/src/decoder/Thread.cxx
+++ b/src/decoder/Thread.cxx
@@ -47,6 +47,42 @@
static constexpr Domain decoder_thread_domain("decoder_thread");
/**
+ * Decode a URI with the given decoder plugin.
+ *
+ * Caller holds DecoderControl::mutex.
+ */
+static bool
+DecoderUriDecode(const DecoderPlugin &plugin,
+ DecoderBridge &bridge, const char *uri)
+{
+ assert(plugin.uri_decode != nullptr);
+ assert(bridge.stream_tag == nullptr);
+ assert(bridge.decoder_tag == nullptr);
+ assert(uri != nullptr);
+ assert(bridge.dc.state == DecoderState::START);
+
+ FormatDebug(decoder_thread_domain, "probing plugin %s", plugin.name);
+
+ if (bridge.dc.command == DecoderCommand::STOP)
+ throw StopDecoder();
+
+ {
+ const ScopeUnlock unlock(bridge.dc.mutex);
+
+ FormatThreadName("decoder:%s", plugin.name);
+
+ plugin.UriDecode(bridge, uri);
+
+ SetThreadName("decoder");
+ }
+
+ assert(bridge.dc.state == DecoderState::START ||
+ bridge.dc.state == DecoderState::DECODE);
+
+ return bridge.dc.state != DecoderState::START;
+}
+
+/**
* Decode a stream with the given decoder plugin.
*
* Caller holds DecoderControl::mutex.
@@ -237,6 +273,24 @@ MaybeLoadReplayGain(DecoderBridge &bridge, InputStream &is)
}
/**
+ * Try decoding a URI.
+ *
+ * DecoderControl::mutex is not be locked by caller.
+ */
+static bool
+TryUriDecode(DecoderBridge &bridge, const char *uri)
+{
+ return decoder_plugins_try([&bridge, uri](const DecoderPlugin &plugin){
+ if (!plugin.SupportsUri(uri))
+ return false;
+
+ std::unique_lock<Mutex> lock(bridge.dc.mutex);
+ bridge.Reset();
+ return DecoderUriDecode(plugin, bridge, uri);
+ });
+}
+
+/**
* Try decoding a stream.
*
* DecoderControl::mutex is not locked by caller.
@@ -244,6 +298,9 @@ MaybeLoadReplayGain(DecoderBridge &bridge, InputStream &is)
static bool
decoder_run_stream(DecoderBridge &bridge, const char *uri)
{
+ if (TryUriDecode(bridge, uri))
+ return true;
+
DecoderControl &dc = bridge.dc;
auto input_stream = bridge.OpenUri(uri);
diff --git a/src/ls.cxx b/src/ls.cxx
index 022ccb370..5a1536c3d 100644
--- a/src/ls.cxx
+++ b/src/ls.cxx
@@ -21,6 +21,8 @@
#include "ls.hxx"
#include "input/Registry.hxx"
#include "input/InputPlugin.hxx"
+#include "decoder/DecoderList.hxx"
+#include "decoder/DecoderPlugin.hxx"
#include "client/Response.hxx"
#include "util/UriExtract.hxx"
@@ -39,6 +41,11 @@ void print_supported_uri_schemes_to_fp(FILE *fp)
protocols.emplace(uri);
});
+ decoder_plugins_for_each([&protocols](const auto &plugin){
+ if (plugin.protocols != nullptr)
+ protocols.merge(plugin.protocols());
+ });
+
for (const auto& protocol : protocols) {
fprintf(fp, " %s", protocol.c_str());
}
@@ -54,6 +61,11 @@ print_supported_uri_schemes(Response &r)
protocols.emplace(uri);
});
+ decoder_plugins_for_each_enabled([&protocols](const auto &plugin){
+ if (plugin.protocols != nullptr)
+ protocols.merge(plugin.protocols());
+ });
+
for (const auto& protocol : protocols) {
r.Format("handler: %s\n", protocol.c_str());
}
@@ -68,5 +80,7 @@ uri_supported_scheme(const char *uri) noexcept
if (plugin->SupportsUri(uri))
return true;
- return false;
+ return decoder_plugins_try([uri](const auto &plugin){
+ return plugin.SupportsUri(uri);
+ });
}
diff --git a/test/run_decoder.cxx b/test/run_decoder.cxx
index 1147e4faf..a7e275302 100644
--- a/test/run_decoder.cxx
+++ b/test/run_decoder.cxx
@@ -204,7 +204,12 @@ try {
}
MyDecoderClient client(c.seek_where);
- if (plugin->file_decode != nullptr) {
+ if (plugin->SupportsUri(c.uri)) {
+ try {
+ plugin->UriDecode(client, c.uri);
+ } catch (StopDecoder) {
+ }
+ } else if (plugin->file_decode != nullptr) {
try {
plugin->FileDecode(client, FromNarrowPath(c.uri));
} catch (StopDecoder) {