From 707d379b97acea36528e5dca4d60e65686926e57 Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Sun, 16 Mar 2014 11:14:16 +0100
Subject: [PATCH] input/mms: move blocking I/O to thread

---
 NEWS                                 |   1 +
 src/input/plugins/MmsInputPlugin.cxx | 110 ++++++++++++---------------
 2 files changed, 51 insertions(+), 60 deletions(-)

diff --git a/NEWS b/NEWS
index 79bb27b5f..896bcee23 100644
--- a/NEWS
+++ b/NEWS
@@ -21,6 +21,7 @@ ver 0.19 (not yet released)
   - read tags from songs in an archive
 * input
   - alsa: new input plugin
+  - mms: non-blocking I/O
   - nfs: new input plugin
   - smbclient: new input plugin
 * filter
diff --git a/src/input/plugins/MmsInputPlugin.cxx b/src/input/plugins/MmsInputPlugin.cxx
index a7068cb2b..845fe5d5d 100644
--- a/src/input/plugins/MmsInputPlugin.cxx
+++ b/src/input/plugins/MmsInputPlugin.cxx
@@ -19,40 +19,56 @@
 
 #include "config.h"
 #include "MmsInputPlugin.hxx"
-#include "../InputStream.hxx"
-#include "../InputPlugin.hxx"
+#include "input/ThreadInputStream.hxx"
+#include "input/InputPlugin.hxx"
 #include "util/StringUtil.hxx"
 #include "util/Error.hxx"
 #include "util/Domain.hxx"
 
 #include <libmms/mmsx.h>
 
-struct MmsInputStream {
-	InputStream base;
+static constexpr size_t MMS_BUFFER_SIZE = 256 * 1024;
 
+class MmsInputStream final : public ThreadInputStream {
 	mmsx_t *mms;
 
-	bool eof;
-
-	MmsInputStream(const char *uri,
-		       Mutex &mutex, Cond &cond,
-		       mmsx_t *_mms)
-		:base(input_plugin_mms, uri, mutex, cond),
-		 mms(_mms), eof(false) {
-		/* XX is this correct?  at least this selects the ffmpeg
-		   decoder, which seems to work fine*/
-		base.mime = "audio/x-ms-wma";
-
-		base.ready = true;
+public:
+	MmsInputStream(const char *uri, Mutex &mutex, Cond &cond)
+		:ThreadInputStream(input_plugin_mms, uri, mutex, cond,
+				   MMS_BUFFER_SIZE) {
 	}
 
-	~MmsInputStream() {
+protected:
+	virtual bool Open(gcc_unused Error &error) override;
+	virtual size_t Read(void *ptr, size_t size, Error &error) override;
+
+	virtual void Close() {
 		mmsx_close(mms);
 	}
 };
 
 static constexpr Domain mms_domain("mms");
 
+bool
+MmsInputStream::Open(Error &error)
+{
+	Unlock();
+
+	mms = mmsx_connect(nullptr, nullptr, GetURI(), 128 * 1024);
+	if (mms == nullptr) {
+		Lock();
+		error.Set(mms_domain, "mmsx_connect() failed");
+		return false;
+	}
+
+	Lock();
+
+	/* TODO: is this correct?  at least this selects the ffmpeg
+	   decoder, which seems to work fine */
+	SetMimeType("audio/x-ms-wma");
+	return true;
+}
+
 static InputStream *
 input_mms_open(const char *url,
 	       Mutex &mutex, Cond &cond,
@@ -64,51 +80,25 @@ input_mms_open(const char *url,
 	    !StringStartsWith(url, "mmsu://"))
 		return nullptr;
 
-	const auto mms = mmsx_connect(nullptr, nullptr, url, 128 * 1024);
-	if (mms == nullptr) {
-		error.Set(mms_domain, "mmsx_connect() failed");
-		return nullptr;
-	}
+	auto m = new MmsInputStream(url, mutex, cond);
+	auto is = m->Start(error);
+	if (is == nullptr)
+		delete m;
 
-	auto m = new MmsInputStream(url, mutex, cond, mms);
-	return &m->base;
+	return is;
 }
 
-static size_t
-input_mms_read(InputStream *is, void *ptr, size_t size,
-	       Error &error)
+size_t
+MmsInputStream::Read(void *ptr, size_t size, Error &error)
 {
-	MmsInputStream *m = (MmsInputStream *)is;
-	int ret;
-
-	ret = mmsx_read(nullptr, m->mms, (char *)ptr, size);
-	if (ret <= 0) {
-		if (ret < 0)
+	int nbytes = mmsx_read(nullptr, mms, (char *)ptr, size);
+	if (nbytes <= 0) {
+		if (nbytes < 0)
 			error.SetErrno("mmsx_read() failed");
-
-		m->eof = true;
-		return false;
+		return 0;
 	}
 
-	is->offset += ret;
-
-	return (size_t)ret;
-}
-
-static void
-input_mms_close(InputStream *is)
-{
-	MmsInputStream *m = (MmsInputStream *)is;
-
-	delete m;
-}
-
-static bool
-input_mms_eof(InputStream *is)
-{
-	MmsInputStream *m = (MmsInputStream *)is;
-
-	return m->eof;
+	return (size_t)nbytes;
 }
 
 const InputPlugin input_plugin_mms = {
@@ -116,12 +106,12 @@ const InputPlugin input_plugin_mms = {
 	nullptr,
 	nullptr,
 	input_mms_open,
-	input_mms_close,
+	ThreadInputStream::Close,
+	ThreadInputStream::Check,
 	nullptr,
 	nullptr,
-	nullptr,
-	nullptr,
-	input_mms_read,
-	input_mms_eof,
+	ThreadInputStream::Available,
+	ThreadInputStream::Read,
+	ThreadInputStream::IsEOF,
 	nullptr,
 };