diff options
author | Max Kellermann <max@musicpd.org> | 2020-09-21 13:01:51 +0200 |
---|---|---|
committer | Max Kellermann <max@musicpd.org> | 2020-09-21 14:53:18 +0200 |
commit | 85849c93964161c15cc0cec7e68bb4917ee93bcd (patch) | |
tree | 86da2f7d606d26521012b5668e96975ea511b679 | |
parent | d3c257d97d4ff7671f7761745fd3c5fd23fecc5f (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.cxx | 4 | ||||
-rw-r--r-- | src/decoder/DecoderPlugin.cxx | 14 | ||||
-rw-r--r-- | src/decoder/DecoderPlugin.hxx | 30 | ||||
-rw-r--r-- | src/decoder/Thread.cxx | 57 | ||||
-rw-r--r-- | src/ls.cxx | 16 | ||||
-rw-r--r-- | test/run_decoder.cxx | 7 |
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) { |