input/Plugin: migrate open() from class Error to C++ exceptions
This commit is contained in:
@@ -81,6 +81,7 @@ AsyncInputStream::Resume()
|
||||
|
||||
if (paused) {
|
||||
paused = false;
|
||||
|
||||
DoResume();
|
||||
}
|
||||
}
|
||||
@@ -264,7 +265,12 @@ AsyncInputStream::DeferredResume()
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
|
||||
Resume();
|
||||
try {
|
||||
Resume();
|
||||
} catch (...) {
|
||||
postponed_exception = std::current_exception();
|
||||
cond.broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -274,10 +280,16 @@ AsyncInputStream::DeferredSeek()
|
||||
if (seek_state != SeekState::SCHEDULED)
|
||||
return;
|
||||
|
||||
Resume();
|
||||
try {
|
||||
Resume();
|
||||
|
||||
seek_state = SeekState::PENDING;
|
||||
buffer.Clear();
|
||||
paused = false;
|
||||
DoSeek(seek_offset);
|
||||
seek_state = SeekState::PENDING;
|
||||
buffer.Clear();
|
||||
paused = false;
|
||||
|
||||
DoSeek(seek_offset);
|
||||
} catch (...) {
|
||||
postponed_exception = std::current_exception();
|
||||
cond.broadcast();
|
||||
}
|
||||
}
|
||||
|
@@ -36,7 +36,6 @@
|
||||
|
||||
struct ConfigBlock;
|
||||
class InputStream;
|
||||
class Error;
|
||||
struct Tag;
|
||||
|
||||
struct InputPlugin {
|
||||
@@ -58,9 +57,11 @@ struct InputPlugin {
|
||||
*/
|
||||
void (*finish)();
|
||||
|
||||
/**
|
||||
* Throws std::runtime_error on error.
|
||||
*/
|
||||
InputStream *(*open)(const char *uri,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error);
|
||||
Mutex &mutex, Cond &cond);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@@ -115,16 +115,17 @@ public:
|
||||
* Opens a new input stream. You may not access it until the "ready"
|
||||
* flag is set.
|
||||
*
|
||||
* Throws std::runtime_error on error.
|
||||
*
|
||||
* @param mutex a mutex that is used to protect this object; must be
|
||||
* locked before calling any of the public methods
|
||||
* @param cond a cond that gets signalled when the state of
|
||||
* this object changes; may be nullptr if the caller doesn't want to get
|
||||
* notifications
|
||||
* @return an #InputStream object on success, nullptr on error
|
||||
* @return an #InputStream object on success
|
||||
*/
|
||||
gcc_nonnull_all
|
||||
static InputStreamPtr Open(const char *uri, Mutex &mutex, Cond &cond,
|
||||
Error &error);
|
||||
static InputStreamPtr Open(const char *uri, Mutex &mutex, Cond &cond);
|
||||
|
||||
/**
|
||||
* Just like Open(), but waits for the stream to become ready.
|
||||
@@ -132,8 +133,7 @@ public:
|
||||
*/
|
||||
gcc_nonnull_all
|
||||
static InputStreamPtr OpenReady(const char *uri,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error);
|
||||
Mutex &mutex, Cond &cond);
|
||||
|
||||
/**
|
||||
* The absolute URI which was used to open this stream.
|
||||
|
@@ -27,7 +27,7 @@
|
||||
#endif
|
||||
|
||||
#include "fs/Path.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "system/Error.hxx"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@@ -36,20 +36,24 @@
|
||||
#endif
|
||||
|
||||
InputStreamPtr
|
||||
OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond, Error &error)
|
||||
OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
assert(!error.IsDefined());
|
||||
InputStreamPtr is;
|
||||
|
||||
InputStreamPtr is(OpenFileInputStream(path, mutex, cond, error));
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
if (is == nullptr && error.IsDomain(errno_domain) &&
|
||||
error.GetCode() == ENOTDIR) {
|
||||
/* ENOTDIR means this may be a path inside an archive
|
||||
file */
|
||||
Error error2;
|
||||
is.reset(OpenArchiveInputStream(path, mutex, cond, error2));
|
||||
if (is == nullptr && error2.IsDefined())
|
||||
error = std::move(error2);
|
||||
try {
|
||||
#endif
|
||||
is = OpenFileInputStream(path, mutex, cond);
|
||||
#ifdef ENABLE_ARCHIVE
|
||||
} catch (const std::system_error &e) {
|
||||
if (IsPathNotFound(e)) {
|
||||
/* ENOTDIR means this may be a path inside an archive
|
||||
file */
|
||||
is = OpenArchiveInputStream(path, mutex, cond);
|
||||
if (!is)
|
||||
throw;
|
||||
} else
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -26,13 +26,14 @@
|
||||
class Path;
|
||||
class Mutex;
|
||||
class Cond;
|
||||
class Error;
|
||||
|
||||
/**
|
||||
* Open a "local" file. This is a wrapper for the input plugins
|
||||
* "file" and "archive".
|
||||
*
|
||||
* Throws std::runtime_error on error.
|
||||
*/
|
||||
InputStreamPtr
|
||||
OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond, Error &error);
|
||||
OpenLocalInputStream(Path path, Mutex &mutex, Cond &cond);
|
||||
|
||||
#endif
|
||||
|
@@ -28,51 +28,51 @@
|
||||
#include "fs/AllocatedPath.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
InputStreamPtr
|
||||
InputStream::Open(const char *url,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (PathTraitsUTF8::IsAbsolute(url)) {
|
||||
Error error;
|
||||
const auto path = AllocatedPath::FromUTF8(url, error);
|
||||
if (path.IsNull())
|
||||
return nullptr;
|
||||
throw std::runtime_error(error.GetMessage());
|
||||
|
||||
return OpenLocalInputStream(path,
|
||||
mutex, cond, error);
|
||||
return OpenLocalInputStream(path, mutex, cond);
|
||||
}
|
||||
|
||||
input_plugins_for_each_enabled(plugin) {
|
||||
InputStream *is;
|
||||
|
||||
is = plugin->open(url, mutex, cond, error);
|
||||
is = plugin->open(url, mutex, cond);
|
||||
if (is != nullptr) {
|
||||
is = input_rewind_open(is);
|
||||
|
||||
return InputStreamPtr(is);
|
||||
} else if (error.IsDefined())
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
error.Set(input_domain, "Unrecognized URI");
|
||||
return nullptr;
|
||||
throw std::runtime_error("Unrecognized URI");
|
||||
}
|
||||
|
||||
InputStreamPtr
|
||||
InputStream::OpenReady(const char *uri,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
auto is = Open(uri, mutex, cond, error);
|
||||
if (is == nullptr)
|
||||
return nullptr;
|
||||
auto is = Open(uri, mutex, cond);
|
||||
|
||||
bool success;
|
||||
|
||||
{
|
||||
const ScopeLock protect(mutex);
|
||||
is->WaitReady();
|
||||
|
||||
Error error;
|
||||
success = is->Check(error);
|
||||
if (!success)
|
||||
throw std::runtime_error(error.GetMessage());
|
||||
}
|
||||
|
||||
if (!success)
|
||||
|
@@ -30,6 +30,7 @@
|
||||
#include "../AsyncInputStream.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/ReusableArray.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
@@ -102,8 +103,7 @@ public:
|
||||
snd_pcm_close(capture_handle);
|
||||
}
|
||||
|
||||
static InputStream *Create(const char *uri, Mutex &mutex, Cond &cond,
|
||||
Error &error);
|
||||
static InputStream *Create(const char *uri, Mutex &mutex, Cond &cond);
|
||||
|
||||
protected:
|
||||
/* virtual methods from AsyncInputStream */
|
||||
@@ -120,8 +120,7 @@ protected:
|
||||
|
||||
private:
|
||||
static snd_pcm_t *OpenDevice(const char *device, int rate,
|
||||
snd_pcm_format_t format, int channels,
|
||||
Error &error);
|
||||
snd_pcm_format_t format, int channels);
|
||||
|
||||
void Pause() {
|
||||
AsyncInputStream::Pause();
|
||||
@@ -143,8 +142,7 @@ private:
|
||||
};
|
||||
|
||||
inline InputStream *
|
||||
AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
const char *device = StringAfterPrefix(uri, "alsa://");
|
||||
if (device == nullptr)
|
||||
@@ -160,10 +158,7 @@ AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond,
|
||||
snd_pcm_format_t format = default_format;
|
||||
int channels = default_channels;
|
||||
|
||||
snd_pcm_t *handle = OpenDevice(device, rate, format, channels,
|
||||
error);
|
||||
if (handle == nullptr)
|
||||
return nullptr;
|
||||
snd_pcm_t *handle = OpenDevice(device, rate, format, channels);
|
||||
|
||||
int frame_size = snd_pcm_format_width(format) / 8 * channels;
|
||||
return new AlsaInputStream(io_thread_get(),
|
||||
@@ -242,47 +237,40 @@ AlsaInputStream::Recover(int err)
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool
|
||||
static void
|
||||
ConfigureCapture(snd_pcm_t *capture_handle,
|
||||
int rate, snd_pcm_format_t format, int channels,
|
||||
Error &error)
|
||||
int rate, snd_pcm_format_t format, int channels)
|
||||
{
|
||||
int err;
|
||||
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot allocate hardware parameter structure (%s)", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0)
|
||||
throw FormatRuntimeError("Cannot allocate hardware parameter structure (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
AtScopeExit(hw_params) {
|
||||
snd_pcm_hw_params_free(hw_params);
|
||||
};
|
||||
|
||||
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot initialize hardware parameter structure (%s)", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_any(capture_handle, hw_params)) < 0)
|
||||
throw FormatRuntimeError("Cannot initialize hardware parameter structure (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot set access type (%s)", snd_strerror (err));
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
|
||||
throw FormatRuntimeError("Cannot set access type (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot set sample format (%s)", snd_strerror (err));
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, format)) < 0)
|
||||
throw FormatRuntimeError("Cannot set sample format (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot set channels (%s)", snd_strerror (err));
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, channels)) < 0)
|
||||
throw FormatRuntimeError("Cannot set channels (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
if ((err = snd_pcm_hw_params_set_rate(capture_handle, hw_params, rate, 0)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot set sample rate (%s)", snd_strerror (err));
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params_set_rate(capture_handle, hw_params, rate, 0)) < 0)
|
||||
throw FormatRuntimeError("Cannot set sample rate (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
/* period needs to be big enough so that poll() doesn't fire too often,
|
||||
* but small enough that buffer overruns don't occur if Read() is not
|
||||
@@ -293,17 +281,13 @@ ConfigureCapture(snd_pcm_t *capture_handle,
|
||||
snd_pcm_uframes_t period = read_buffer_size * 2;
|
||||
int direction = -1;
|
||||
if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params,
|
||||
&period, &direction)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot set period size (%s)",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
&period, &direction)) < 0)
|
||||
throw FormatRuntimeError("Cannot set period size (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) {
|
||||
error.Format(alsa_input_domain, "Cannot set parameters (%s)",
|
||||
snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0)
|
||||
throw FormatRuntimeError("Cannot set parameters (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
|
||||
@@ -315,37 +299,31 @@ ConfigureCapture(snd_pcm_t *capture_handle,
|
||||
};
|
||||
|
||||
if ((err = snd_pcm_sw_params_set_start_threshold(capture_handle, sw_params,
|
||||
period)) < 0) {
|
||||
error.Format(alsa_input_domain,
|
||||
"unable to set start threshold (%s)", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
period)) < 0)
|
||||
throw FormatRuntimeError("unable to set start threshold (%s)",
|
||||
snd_strerror(err));
|
||||
|
||||
if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0) {
|
||||
error.Format(alsa_input_domain,
|
||||
"unable to install sw params (%s)", snd_strerror(err));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0)
|
||||
throw FormatRuntimeError("unable to install sw params (%s)",
|
||||
snd_strerror(err));
|
||||
}
|
||||
|
||||
inline snd_pcm_t *
|
||||
AlsaInputStream::OpenDevice(const char *device,
|
||||
int rate, snd_pcm_format_t format, int channels,
|
||||
Error &error)
|
||||
int rate, snd_pcm_format_t format, int channels)
|
||||
{
|
||||
snd_pcm_t *capture_handle;
|
||||
int err;
|
||||
if ((err = snd_pcm_open(&capture_handle, device,
|
||||
SND_PCM_STREAM_CAPTURE, 0)) < 0) {
|
||||
error.Format(alsa_input_domain, "Failed to open device: %s (%s)", device, snd_strerror(err));
|
||||
return nullptr;
|
||||
}
|
||||
SND_PCM_STREAM_CAPTURE, 0)) < 0)
|
||||
throw FormatRuntimeError("Failed to open device: %s (%s)",
|
||||
device, snd_strerror(err));
|
||||
|
||||
if (!ConfigureCapture(capture_handle, rate, format, channels, error)) {
|
||||
try {
|
||||
ConfigureCapture(capture_handle, rate, format, channels);
|
||||
} catch (...) {
|
||||
snd_pcm_close(capture_handle);
|
||||
return nullptr;
|
||||
throw;
|
||||
}
|
||||
|
||||
snd_pcm_prepare(capture_handle);
|
||||
@@ -356,9 +334,9 @@ AlsaInputStream::OpenDevice(const char *device,
|
||||
/*######################### Plugin Functions ##############################*/
|
||||
|
||||
static InputStream *
|
||||
alsa_input_open(const char *uri, Mutex &mutex, Cond &cond, Error &error)
|
||||
alsa_input_open(const char *uri, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
return AlsaInputStream::Create(uri, mutex, cond, error);
|
||||
return AlsaInputStream::Create(uri, mutex, cond);
|
||||
}
|
||||
|
||||
const struct InputPlugin input_plugin_alsa = {
|
||||
|
@@ -25,14 +25,18 @@
|
||||
#include "archive/ArchivePlugin.hxx"
|
||||
#include "archive/ArchiveFile.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "../InputStream.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "Log.hxx"
|
||||
#include "util/ScopeExit.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
InputStream *
|
||||
OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond, Error &error)
|
||||
InputStreamPtr
|
||||
OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
const ArchivePlugin *arplug;
|
||||
|
||||
@@ -57,22 +61,21 @@ OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond, Error &error)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Error error;
|
||||
auto file = archive_file_open(arplug, Path::FromFS(archive), error);
|
||||
if (file == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (file == nullptr)
|
||||
throw std::runtime_error(error.GetMessage());
|
||||
|
||||
AtScopeExit(file) {
|
||||
file->Close();
|
||||
};
|
||||
|
||||
return file->OpenStream(filename, mutex, cond, error);
|
||||
return InputStreamPtr(file->OpenStream(filename, mutex, cond));
|
||||
}
|
||||
|
||||
static InputStream *
|
||||
input_archive_open(gcc_unused const char *filename,
|
||||
gcc_unused Mutex &mutex, gcc_unused Cond &cond,
|
||||
gcc_unused Error &error)
|
||||
gcc_unused Mutex &mutex, gcc_unused Cond &cond)
|
||||
{
|
||||
/* dummy method; use OpenArchiveInputStream() instead */
|
||||
|
||||
|
@@ -20,15 +20,15 @@
|
||||
#ifndef MPD_INPUT_ARCHIVE_HXX
|
||||
#define MPD_INPUT_ARCHIVE_HXX
|
||||
|
||||
class InputStream;
|
||||
#include "input/Ptr.hxx"
|
||||
|
||||
class Path;
|
||||
class Mutex;
|
||||
class Cond;
|
||||
class Error;
|
||||
|
||||
extern const struct InputPlugin input_plugin_archive;
|
||||
|
||||
InputStream *
|
||||
OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond, Error &error);
|
||||
InputStreamPtr
|
||||
OpenArchiveInputStream(Path path, Mutex &mutex, Cond &cond);
|
||||
|
||||
#endif
|
||||
|
@@ -127,7 +127,7 @@ struct cdio_uri {
|
||||
};
|
||||
|
||||
static bool
|
||||
parse_cdio_uri(struct cdio_uri *dest, const char *src, Error &error)
|
||||
parse_cdio_uri(struct cdio_uri *dest, const char *src)
|
||||
{
|
||||
if (!StringStartsWith(src, "cdda://"))
|
||||
return false;
|
||||
@@ -160,10 +160,8 @@ parse_cdio_uri(struct cdio_uri *dest, const char *src, Error &error)
|
||||
|
||||
char *endptr;
|
||||
dest->track = strtoul(track, &endptr, 10);
|
||||
if (*endptr != 0) {
|
||||
error.Set(cdio_domain, "Malformed track number");
|
||||
return false;
|
||||
}
|
||||
if (*endptr != 0)
|
||||
throw std::runtime_error("Malformed track number");
|
||||
|
||||
if (endptr == track)
|
||||
/* play the whole CD */
|
||||
@@ -187,35 +185,28 @@ cdio_detect_device(void)
|
||||
|
||||
static InputStream *
|
||||
input_cdio_open(const char *uri,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
struct cdio_uri parsed_uri;
|
||||
if (!parse_cdio_uri(&parsed_uri, uri, error))
|
||||
if (!parse_cdio_uri(&parsed_uri, uri))
|
||||
return nullptr;
|
||||
|
||||
/* get list of CD's supporting CD-DA */
|
||||
const AllocatedPath device = parsed_uri.device[0] != 0
|
||||
? AllocatedPath::FromFS(parsed_uri.device)
|
||||
: cdio_detect_device();
|
||||
if (device.IsNull()) {
|
||||
error.Set(cdio_domain,
|
||||
"Unable find or access a CD-ROM drive with an audio CD in it.");
|
||||
return nullptr;
|
||||
}
|
||||
if (device.IsNull())
|
||||
throw std::runtime_error("Unable find or access a CD-ROM drive with an audio CD in it.");
|
||||
|
||||
/* Found such a CD-ROM with a CD-DA loaded. Use the first drive in the list. */
|
||||
const auto cdio = cdio_open(device.c_str(), DRIVER_UNKNOWN);
|
||||
if (cdio == nullptr) {
|
||||
error.Set(cdio_domain, "Failed to open CD drive");
|
||||
return nullptr;
|
||||
}
|
||||
if (cdio == nullptr)
|
||||
throw std::runtime_error("Failed to open CD drive");
|
||||
|
||||
const auto drv = cdio_cddap_identify_cdio(cdio, 1, nullptr);
|
||||
if (drv == nullptr) {
|
||||
error.Set(cdio_domain, "Unable to identify audio CD disc.");
|
||||
cdio_destroy(cdio);
|
||||
return nullptr;
|
||||
throw std::runtime_error("Unable to identify audio CD disc.");
|
||||
}
|
||||
|
||||
cdda_verbose_set(drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
|
||||
@@ -223,12 +214,12 @@ input_cdio_open(const char *uri,
|
||||
if (0 != cdio_cddap_open(drv)) {
|
||||
cdio_cddap_close_no_free_cdio(drv);
|
||||
cdio_destroy(cdio);
|
||||
error.Set(cdio_domain, "Unable to open disc.");
|
||||
return nullptr;
|
||||
throw std::runtime_error("Unable to open disc.");
|
||||
}
|
||||
|
||||
bool reverse_endian;
|
||||
switch (data_bigendianp(drv)) {
|
||||
const int be = data_bigendianp(drv);
|
||||
switch (be) {
|
||||
case -1:
|
||||
LogDebug(cdio_domain, "drive returns unknown audio data");
|
||||
reverse_endian = default_reverse_endian;
|
||||
@@ -245,11 +236,10 @@ input_cdio_open(const char *uri,
|
||||
break;
|
||||
|
||||
default:
|
||||
error.Format(cdio_domain, "Drive returns unknown data type %d",
|
||||
data_bigendianp(drv));
|
||||
cdio_cddap_close_no_free_cdio(drv);
|
||||
cdio_destroy(cdio);
|
||||
return nullptr;
|
||||
throw FormatRuntimeError("Drive returns unknown data type %d",
|
||||
be);
|
||||
}
|
||||
|
||||
lsn_t lsn_from, lsn_to;
|
||||
|
@@ -32,6 +32,7 @@
|
||||
#include "util/ASCII.hxx"
|
||||
#include "util/StringUtil.hxx"
|
||||
#include "util/NumberParser.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "Log.hxx"
|
||||
@@ -78,17 +79,18 @@ struct CurlInputStream final : public AsyncInputStream {
|
||||
CURL_MAX_BUFFERED,
|
||||
CURL_RESUME_AT),
|
||||
request_headers(nullptr),
|
||||
icy(new IcyInputStream(this)) {}
|
||||
icy(new IcyInputStream(this)) {
|
||||
InitEasy();
|
||||
}
|
||||
|
||||
~CurlInputStream();
|
||||
|
||||
CurlInputStream(const CurlInputStream &) = delete;
|
||||
CurlInputStream &operator=(const CurlInputStream &) = delete;
|
||||
|
||||
static InputStream *Open(const char *url, Mutex &mutex, Cond &cond,
|
||||
Error &error);
|
||||
static InputStream *Open(const char *url, Mutex &mutex, Cond &cond);
|
||||
|
||||
bool InitEasy(Error &error);
|
||||
void InitEasy();
|
||||
|
||||
/**
|
||||
* Frees the current "libcurl easy" handle, and everything
|
||||
@@ -201,7 +203,7 @@ public:
|
||||
curl_multi_cleanup(multi);
|
||||
}
|
||||
|
||||
bool Add(CurlInputStream *c, Error &error);
|
||||
void Add(CurlInputStream *c);
|
||||
void Remove(CurlInputStream *c);
|
||||
|
||||
/**
|
||||
@@ -355,41 +357,39 @@ CurlSocket::OnSocketReady(unsigned flags)
|
||||
|
||||
/**
|
||||
* Runs in the I/O thread. No lock needed.
|
||||
*
|
||||
* Throws std::runtime_error on error.
|
||||
*/
|
||||
inline bool
|
||||
CurlMulti::Add(CurlInputStream *c, Error &error)
|
||||
inline void
|
||||
CurlMulti::Add(CurlInputStream *c)
|
||||
{
|
||||
assert(io_thread_inside());
|
||||
assert(c != nullptr);
|
||||
assert(c->easy != nullptr);
|
||||
|
||||
CURLMcode mcode = curl_multi_add_handle(multi, c->easy);
|
||||
if (mcode != CURLM_OK) {
|
||||
error.Format(curlm_domain, mcode,
|
||||
"curl_multi_add_handle() failed: %s",
|
||||
curl_multi_strerror(mcode));
|
||||
return false;
|
||||
}
|
||||
if (mcode != CURLM_OK)
|
||||
throw FormatRuntimeError("curl_multi_add_handle() failed: %s",
|
||||
curl_multi_strerror(mcode));
|
||||
|
||||
InvalidateSockets();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call input_curl_easy_add() in the I/O thread. May be called from
|
||||
* any thread. Caller must not hold a mutex.
|
||||
*
|
||||
* Throws std::runtime_error on error.
|
||||
*/
|
||||
static bool
|
||||
input_curl_easy_add_indirect(CurlInputStream *c, Error &error)
|
||||
static void
|
||||
input_curl_easy_add_indirect(CurlInputStream *c)
|
||||
{
|
||||
assert(c != nullptr);
|
||||
assert(c->easy != nullptr);
|
||||
|
||||
bool result;
|
||||
BlockingCall(io_thread_get(), [c, &error, &result](){
|
||||
result = curl_multi->Add(c, error);
|
||||
BlockingCall(io_thread_get(), [c](){
|
||||
curl_multi->Add(c);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void
|
||||
@@ -727,14 +727,12 @@ input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
|
||||
return c.DataReceived(ptr, size);
|
||||
}
|
||||
|
||||
bool
|
||||
CurlInputStream::InitEasy(Error &error)
|
||||
void
|
||||
CurlInputStream::InitEasy()
|
||||
{
|
||||
easy = curl_easy_init();
|
||||
if (easy == nullptr) {
|
||||
error.Set(curl_domain, "curl_easy_init() failed");
|
||||
return false;
|
||||
}
|
||||
if (easy == nullptr)
|
||||
throw std::runtime_error("curl_easy_init() failed");
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this);
|
||||
curl_easy_setopt(easy, CURLOPT_USERAGENT,
|
||||
@@ -773,19 +771,14 @@ CurlInputStream::InitEasy(Error &error)
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_VERIFYHOST, verify_host ? 2l : 0l);
|
||||
|
||||
CURLcode code = curl_easy_setopt(easy, CURLOPT_URL, GetURI());
|
||||
if (code != CURLE_OK) {
|
||||
error.Format(curl_domain, code,
|
||||
"curl_easy_setopt() failed: %s",
|
||||
curl_easy_strerror(code));
|
||||
return false;
|
||||
}
|
||||
if (code != CURLE_OK)
|
||||
throw FormatRuntimeError("curl_easy_setopt() failed: %s",
|
||||
curl_easy_strerror(code));
|
||||
|
||||
request_headers = nullptr;
|
||||
request_headers = curl_slist_append(request_headers,
|
||||
"Icy-Metadata: 1");
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPHEADER, request_headers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -809,11 +802,11 @@ CurlInputStream::DoSeek(offset_type new_offset)
|
||||
return;
|
||||
}
|
||||
|
||||
Error error;
|
||||
if (!InitEasy(postponed_error)) {
|
||||
try {
|
||||
InitEasy();
|
||||
} catch (...) {
|
||||
mutex.lock();
|
||||
PostponeError(std::move(error));
|
||||
return;
|
||||
throw;
|
||||
}
|
||||
|
||||
/* send the "Range" header */
|
||||
@@ -823,10 +816,11 @@ CurlInputStream::DoSeek(offset_type new_offset)
|
||||
curl_easy_setopt(easy, CURLOPT_RANGE, range);
|
||||
}
|
||||
|
||||
if (!input_curl_easy_add_indirect(this, error)) {
|
||||
try {
|
||||
input_curl_easy_add_indirect(this);
|
||||
} catch (...) {
|
||||
mutex.lock();
|
||||
PostponeError(std::move(error));
|
||||
return;
|
||||
throw;
|
||||
}
|
||||
|
||||
mutex.lock();
|
||||
@@ -834,27 +828,29 @@ CurlInputStream::DoSeek(offset_type new_offset)
|
||||
}
|
||||
|
||||
inline InputStream *
|
||||
CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
CurlInputStream::Open(const char *url, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
CurlInputStream *c = new CurlInputStream(url, mutex, cond);
|
||||
if (!c->InitEasy(error) || !input_curl_easy_add_indirect(c, error)) {
|
||||
|
||||
try {
|
||||
c->InitEasy();
|
||||
input_curl_easy_add_indirect(c);
|
||||
} catch (...) {
|
||||
delete c;
|
||||
return nullptr;
|
||||
throw;
|
||||
}
|
||||
|
||||
return c->icy;
|
||||
}
|
||||
|
||||
static InputStream *
|
||||
input_curl_open(const char *url, Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
input_curl_open(const char *url, Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (memcmp(url, "http://", 7) != 0 &&
|
||||
memcmp(url, "https://", 8) != 0)
|
||||
return nullptr;
|
||||
|
||||
return CurlInputStream::Open(url, mutex, cond, error);
|
||||
return CurlInputStream::Open(url, mutex, cond);
|
||||
}
|
||||
|
||||
const struct InputPlugin input_plugin_curl = {
|
||||
|
@@ -84,8 +84,7 @@ input_ffmpeg_init(gcc_unused const ConfigBlock &block)
|
||||
|
||||
static InputStream *
|
||||
input_ffmpeg_open(const char *uri,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(uri, "gopher://") &&
|
||||
!StringStartsWith(uri, "rtp://") &&
|
||||
@@ -97,10 +96,8 @@ input_ffmpeg_open(const char *uri,
|
||||
|
||||
AVIOContext *h;
|
||||
auto result = avio_open(&h, uri, AVIO_FLAG_READ);
|
||||
if (result != 0) {
|
||||
SetFfmpegError(error, result);
|
||||
return nullptr;
|
||||
}
|
||||
if (result != 0)
|
||||
throw MakeFfmpegError(result);
|
||||
|
||||
return new FfmpegInputStream(uri, mutex, cond, h);
|
||||
}
|
||||
|
@@ -21,18 +21,15 @@
|
||||
#include "FileInputPlugin.hxx"
|
||||
#include "../InputStream.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "util/Error.hxx"
|
||||
#include "util/Domain.hxx"
|
||||
#include "fs/Path.hxx"
|
||||
#include "fs/FileInfo.hxx"
|
||||
#include "fs/io/FileReader.hxx"
|
||||
#include "system/FileDescriptor.hxx"
|
||||
#include "util/RuntimeError.hxx"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static constexpr Domain file_domain("file");
|
||||
|
||||
class FileInputStream final : public InputStream {
|
||||
FileReader reader;
|
||||
|
||||
@@ -56,38 +53,31 @@ public:
|
||||
bool Seek(offset_type offset, Error &error) override;
|
||||
};
|
||||
|
||||
InputStream *
|
||||
InputStreamPtr
|
||||
OpenFileInputStream(Path path,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
try {
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
FileReader reader(path);
|
||||
|
||||
const FileInfo info = reader.GetFileInfo();
|
||||
|
||||
if (!info.IsRegular()) {
|
||||
error.Format(file_domain, "Not a regular file: %s",
|
||||
path.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
if (!info.IsRegular())
|
||||
throw FormatRuntimeError("Not a regular file: %s",
|
||||
path.c_str());
|
||||
|
||||
#ifdef POSIX_FADV_SEQUENTIAL
|
||||
posix_fadvise(reader.GetFD().Get(), (off_t)0, info.GetSize(),
|
||||
POSIX_FADV_SEQUENTIAL);
|
||||
#endif
|
||||
|
||||
return new FileInputStream(path.ToUTF8().c_str(),
|
||||
std::move(reader), info.GetSize(),
|
||||
mutex, cond);
|
||||
} catch (const std::exception &e) {
|
||||
error.Set(std::current_exception());
|
||||
return nullptr;
|
||||
return InputStreamPtr(new FileInputStream(path.ToUTF8().c_str(),
|
||||
std::move(reader), info.GetSize(),
|
||||
mutex, cond));
|
||||
}
|
||||
|
||||
static InputStream *
|
||||
input_file_open(gcc_unused const char *filename,
|
||||
gcc_unused Mutex &mutex, gcc_unused Cond &cond,
|
||||
gcc_unused Error &error)
|
||||
gcc_unused Mutex &mutex, gcc_unused Cond &cond)
|
||||
{
|
||||
/* dummy method; use OpenFileInputStream() instead */
|
||||
|
||||
|
@@ -20,17 +20,16 @@
|
||||
#ifndef MPD_INPUT_FILE_HXX
|
||||
#define MPD_INPUT_FILE_HXX
|
||||
|
||||
class InputStream;
|
||||
#include "input/Ptr.hxx"
|
||||
|
||||
class Path;
|
||||
class Mutex;
|
||||
class Cond;
|
||||
class Error;
|
||||
|
||||
extern const struct InputPlugin input_plugin_file;
|
||||
|
||||
InputStream *
|
||||
InputStreamPtr
|
||||
OpenFileInputStream(Path path,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error);
|
||||
Mutex &mutex, Cond &cond);
|
||||
|
||||
#endif
|
||||
|
@@ -72,8 +72,7 @@ MmsInputStream::Open(Error &error)
|
||||
|
||||
static InputStream *
|
||||
input_mms_open(const char *url,
|
||||
Mutex &mutex, Cond &cond,
|
||||
gcc_unused Error &error)
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(url, "mms://") &&
|
||||
!StringStartsWith(url, "mmsh://") &&
|
||||
|
@@ -57,10 +57,10 @@ public:
|
||||
DeferClose();
|
||||
}
|
||||
|
||||
bool Open(Error &error) {
|
||||
void Open() {
|
||||
assert(!IsReady());
|
||||
|
||||
return NfsFileReader::Open(GetURI(), error);
|
||||
NfsFileReader::Open(GetURI());
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -119,17 +119,10 @@ NfsInputStream::DoResume()
|
||||
reconnect_on_resume = false;
|
||||
reconnecting = true;
|
||||
|
||||
mutex.unlock();
|
||||
ScopeUnlock unlock(mutex);
|
||||
|
||||
NfsFileReader::Close();
|
||||
|
||||
Error error;
|
||||
bool success = NfsFileReader::Open(GetURI(), error);
|
||||
mutex.lock();
|
||||
|
||||
if (!success) {
|
||||
postponed_error = std::move(error);
|
||||
cond.broadcast();
|
||||
}
|
||||
NfsFileReader::Open(GetURI());
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -229,16 +222,17 @@ input_nfs_finish()
|
||||
|
||||
static InputStream *
|
||||
input_nfs_open(const char *uri,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(uri, "nfs://"))
|
||||
return nullptr;
|
||||
|
||||
NfsInputStream *is = new NfsInputStream(uri, mutex, cond);
|
||||
if (!is->Open(error)) {
|
||||
try {
|
||||
is->Open();
|
||||
} catch (...) {
|
||||
delete is;
|
||||
return nullptr;
|
||||
throw;
|
||||
}
|
||||
|
||||
return is;
|
||||
|
@@ -24,6 +24,7 @@
|
||||
#include "../InputStream.hxx"
|
||||
#include "../InputPlugin.hxx"
|
||||
#include "PluginUnavailable.hxx"
|
||||
#include "system/Error.hxx"
|
||||
#include "util/StringCompare.hxx"
|
||||
#include "util/Error.hxx"
|
||||
|
||||
@@ -85,8 +86,7 @@ input_smbclient_init(gcc_unused const ConfigBlock &block)
|
||||
|
||||
static InputStream *
|
||||
input_smbclient_open(const char *uri,
|
||||
Mutex &mutex, Cond &cond,
|
||||
Error &error)
|
||||
Mutex &mutex, Cond &cond)
|
||||
{
|
||||
if (!StringStartsWith(uri, "smb://"))
|
||||
return nullptr;
|
||||
@@ -94,33 +94,30 @@ input_smbclient_open(const char *uri,
|
||||
const ScopeLock protect(smbclient_mutex);
|
||||
|
||||
SMBCCTX *ctx = smbc_new_context();
|
||||
if (ctx == nullptr) {
|
||||
error.SetErrno("smbc_new_context() failed");
|
||||
return nullptr;
|
||||
}
|
||||
if (ctx == nullptr)
|
||||
throw MakeErrno("smbc_new_context() failed");
|
||||
|
||||
SMBCCTX *ctx2 = smbc_init_context(ctx);
|
||||
if (ctx2 == nullptr) {
|
||||
error.SetErrno("smbc_init_context() failed");
|
||||
int e = errno;
|
||||
smbc_free_context(ctx, 1);
|
||||
return nullptr;
|
||||
throw MakeErrno(e, "smbc_init_context() failed");
|
||||
}
|
||||
|
||||
ctx = ctx2;
|
||||
|
||||
int fd = smbc_open(uri, O_RDONLY, 0);
|
||||
if (fd < 0) {
|
||||
error.SetErrno("smbc_open() failed");
|
||||
int e = errno;
|
||||
smbc_free_context(ctx, 1);
|
||||
return nullptr;
|
||||
throw MakeErrno(e, "smbc_open() failed");
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (smbc_fstat(fd, &st) < 0) {
|
||||
error.SetErrno("smbc_fstat() failed");
|
||||
smbc_close(fd);
|
||||
int e = errno;
|
||||
smbc_free_context(ctx, 1);
|
||||
return nullptr;
|
||||
throw MakeErrno(e, "smbc_fstat() failed");
|
||||
}
|
||||
|
||||
return new SmbclientInputStream(uri, mutex, cond, ctx, fd, st);
|
||||
|
Reference in New Issue
Block a user