diff options
Diffstat (limited to 'src/input')
-rw-r--r-- | src/input/AlsaInputPlugin.cxx | 206 | ||||
-rw-r--r-- | src/input/AlsaInputPlugin.hxx | 28 | ||||
-rw-r--r-- | src/input/ArchiveInputPlugin.cxx | 3 | ||||
-rw-r--r-- | src/input/CdioParanoiaInputPlugin.cxx | 3 | ||||
-rw-r--r-- | src/input/CurlInputPlugin.cxx | 14 | ||||
-rw-r--r-- | src/input/DespotifyInputPlugin.cxx | 123 | ||||
-rw-r--r-- | src/input/FfmpegInputPlugin.cxx | 16 | ||||
-rw-r--r-- | src/input/FileInputPlugin.cxx | 4 | ||||
-rw-r--r-- | src/input/MmsInputPlugin.cxx | 13 | ||||
-rw-r--r-- | src/input/RewindInputPlugin.cxx | 1 |
10 files changed, 335 insertions, 76 deletions
diff --git a/src/input/AlsaInputPlugin.cxx b/src/input/AlsaInputPlugin.cxx new file mode 100644 index 000000000..c910ae340 --- /dev/null +++ b/src/input/AlsaInputPlugin.cxx @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +/* + * ALSA code based on an example by Paul Davis released under GPL here: + * http://equalarea.com/paul/alsa-audio.html + * and one by Matthias Nagorni, also GPL, here: + * http://alsamodular.sourceforge.net/alsa_programming_howto.html + */ + +#include "config.h" +#include "AlsaInputPlugin.hxx" +#include "InputPlugin.hxx" +#include "InputStream.hxx" +#include "util/Domain.hxx" +#include "util/Error.hxx" +#include "util/StringUtil.hxx" +#include "Log.hxx" + +#include <alsa/asoundlib.h> + +static constexpr Domain alsa_input_domain("alsa"); + +static constexpr const char *default_device = "hw:0,0"; + +// this value chosen to balance between limiting latency and avoiding stutter +static constexpr int max_frames_to_buffer = 64; + +// the following defaults are because the PcmDecoderPlugin forces CD format +static constexpr snd_pcm_format_t default_format = SND_PCM_FORMAT_S16; +static constexpr int default_channels = 2; // stereo +static constexpr unsigned int default_rate = 44100; // cd quality + +struct AlsaInputStream { + InputStream base; + snd_pcm_t *capture_handle; + size_t frame_size; + size_t max_bytes_to_read; + + AlsaInputStream(const char *uri, Mutex &mutex, Cond &cond, + snd_pcm_t *handle) + :base(input_plugin_alsa, uri, mutex, cond), + capture_handle(handle) { + frame_size = snd_pcm_format_width(default_format) / 8 * default_channels; + max_bytes_to_read = max_frames_to_buffer * frame_size; + base.mime = strdup("audio/x-mpd-cdda-pcm"); + base.seekable = false; + base.size = -1; + base.ready = true; + } + + ~AlsaInputStream() { + snd_pcm_close(capture_handle); + } +}; + +static InputStream * +alsa_input_open(const char *uri, Mutex &mutex, Cond &cond, Error &error) +{ + int err; + + // check uri is appropriate for alsa input + if (!StringStartsWith(uri, "alsa://")) + return nullptr; + + const char *device = uri + 7; + if (device[0] == '\0') + device = default_device; + + snd_pcm_t *capture_handle; + 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_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)); + snd_pcm_close(capture_handle); + return nullptr; + } + + 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)); + snd_pcm_hw_params_free(hw_params); + snd_pcm_close(capture_handle); + return nullptr; + } + + 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)); + snd_pcm_hw_params_free (hw_params); + snd_pcm_close(capture_handle); + return nullptr; + } + + if ((err = snd_pcm_hw_params_set_format(capture_handle, hw_params, default_format)) < 0) { + snd_pcm_hw_params_free(hw_params); + snd_pcm_close(capture_handle); + error.Format(alsa_input_domain, "Cannot set sample format (%s)", snd_strerror (err)); + return nullptr; + } + + if ((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params, default_channels)) < 0) { + snd_pcm_hw_params_free(hw_params); + snd_pcm_close(capture_handle); + error.Format(alsa_input_domain, "Cannot set channels (%s)", snd_strerror (err)); + return nullptr; + } + + if ((err = snd_pcm_hw_params_set_rate(capture_handle, hw_params, default_rate, 0)) < 0) { + snd_pcm_hw_params_free(hw_params); + snd_pcm_close(capture_handle); + error.Format(alsa_input_domain, "Cannot set sample rate (%s)", snd_strerror (err)); + return nullptr; + } + + if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) { + error.Format(alsa_input_domain, "Cannot set parameters (%s)", snd_strerror (err)); + snd_pcm_hw_params_free(hw_params); + snd_pcm_close(capture_handle); + return nullptr; + } + + snd_pcm_hw_params_free (hw_params); + + // clear any data already in the PCM buffer + if ((err = snd_pcm_drop(capture_handle)) < 0) { + error.Format(alsa_input_domain, "Cannot clear PCM buffer (%s)", snd_strerror (err)); + snd_pcm_hw_params_free(hw_params); + snd_pcm_close(capture_handle); + return nullptr; + } + + AlsaInputStream *ais = new AlsaInputStream(uri, mutex, cond, capture_handle); + return &ais->base; +} + +static void +alsa_input_close(InputStream *is) +{ + AlsaInputStream *ais = (AlsaInputStream*) is; + delete ais; +} + +static size_t +alsa_input_read(InputStream *is, void *ptr, size_t size, + gcc_unused Error &error) +{ + AlsaInputStream *ais = (AlsaInputStream*) is; + int num_frames = max_frames_to_buffer; + if (size < ais->max_bytes_to_read) + // calculate number of whole frames that will fit in size bytes + num_frames = size / ais->frame_size; + + int ret; + while ((ret = snd_pcm_readi(ais->capture_handle, ptr, + num_frames)) < 0) { + snd_pcm_prepare(ais->capture_handle); + LogDebug(alsa_input_domain, "Buffer Overrun"); + } + + size_t nbytes = ret == max_frames_to_buffer + ? ais->max_bytes_to_read + : ret * ais->frame_size; + is->offset += nbytes; + return nbytes; +} + +static bool +alsa_input_eof(gcc_unused InputStream *is) +{ + return false; +}; + +const struct InputPlugin input_plugin_alsa = { + "alsa", + nullptr, + nullptr, + alsa_input_open, + alsa_input_close, + nullptr, + nullptr, + nullptr, + nullptr, + alsa_input_read, + alsa_input_eof, + nullptr, +}; diff --git a/src/input/AlsaInputPlugin.hxx b/src/input/AlsaInputPlugin.hxx new file mode 100644 index 000000000..ac9519588 --- /dev/null +++ b/src/input/AlsaInputPlugin.hxx @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2003-2013 The Music Player Daemon Project + * http://www.musicpd.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPD_ALSA_INPUT_PLUGIN_HXX +#define MPD_ALSA_INPUT_PLUGIN_HXX + +#include "InputPlugin.hxx" + +extern const struct InputPlugin input_plugin_alsa; + + +#endif diff --git a/src/input/ArchiveInputPlugin.cxx b/src/input/ArchiveInputPlugin.cxx index 5288f2b3b..5797caced 100644 --- a/src/input/ArchiveInputPlugin.cxx +++ b/src/input/ArchiveInputPlugin.cxx @@ -25,7 +25,6 @@ #include "ArchivePlugin.hxx" #include "ArchiveFile.hxx" #include "InputPlugin.hxx" -#include "util/Error.hxx" #include "fs/Traits.hxx" #include "Log.hxx" @@ -47,7 +46,7 @@ input_archive_open(const char *pathname, const struct archive_plugin *arplug; InputStream *is; - if (!PathTraits::IsAbsoluteFS(pathname)) + if (!PathTraitsFS::IsAbsolute(pathname)) return nullptr; char *pname = g_strdup(pathname); diff --git a/src/input/CdioParanoiaInputPlugin.cxx b/src/input/CdioParanoiaInputPlugin.cxx index b3ac57413..bf1c3c908 100644 --- a/src/input/CdioParanoiaInputPlugin.cxx +++ b/src/input/CdioParanoiaInputPlugin.cxx @@ -25,6 +25,7 @@ #include "CdioParanoiaInputPlugin.hxx" #include "InputStream.hxx" #include "InputPlugin.hxx" +#include "util/StringUtil.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" #include "system/ByteOrder.hxx" @@ -122,7 +123,7 @@ struct cdio_uri { static bool parse_cdio_uri(struct cdio_uri *dest, const char *src, Error &error) { - if (!g_str_has_prefix(src, "cdda://")) + if (!StringStartsWith(src, "cdda://")) return false; src += 7; diff --git a/src/input/CurlInputPlugin.cxx b/src/input/CurlInputPlugin.cxx index b78545951..fa8f9affc 100644 --- a/src/input/CurlInputPlugin.cxx +++ b/src/input/CurlInputPlugin.cxx @@ -24,6 +24,7 @@ #include "ConfigGlobal.hxx" #include "ConfigData.hxx" #include "tag/Tag.hxx" +#include "tag/TagBuilder.hxx" #include "IcyMetaDataParser.hxx" #include "event/SocketMonitor.hxx" #include "event/TimeoutMonitor.hxx" @@ -780,8 +781,11 @@ copy_icy_tag(struct input_curl *c) delete c->tag; - if (!c->meta_name.empty() && !tag->HasType(TAG_NAME)) - tag->AddItem(TAG_NAME, c->meta_name.c_str()); + if (!c->meta_name.empty() && !tag->HasType(TAG_NAME)) { + TagBuilder tag_builder(std::move(*tag)); + tag_builder.AddItem(TAG_NAME, c->meta_name.c_str()); + tag_builder.Commit(*tag); + } c->tag = tag; } @@ -910,8 +914,10 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) delete c->tag; - c->tag = new Tag(); - c->tag->AddItem(TAG_NAME, c->meta_name.c_str()); + TagBuilder tag_builder; + tag_builder.AddItem(TAG_NAME, c->meta_name.c_str()); + + c->tag = tag_builder.Commit(); } else if (StringEqualsCaseASCII(name, "icy-metaint")) { char buffer[64]; size_t icy_metaint; diff --git a/src/input/DespotifyInputPlugin.cxx b/src/input/DespotifyInputPlugin.cxx index b08299516..38c2aac75 100644 --- a/src/input/DespotifyInputPlugin.cxx +++ b/src/input/DespotifyInputPlugin.cxx @@ -23,21 +23,20 @@ #include "InputStream.hxx" #include "InputPlugin.hxx" #include "tag/Tag.hxx" +#include "util/StringUtil.hxx" #include "Log.hxx" extern "C" { #include <despotify.h> } -#include <glib.h> - #include <unistd.h> #include <string.h> #include <errno.h> #include <stdio.h> -struct DespotifyInputStream { +class DespotifyInputStream { InputStream base; struct despotify_session *session; @@ -63,30 +62,52 @@ struct DespotifyInputStream { base.ready = true; } +public: ~DespotifyInputStream() { delete tag; despotify_free_track(track); } + + static InputStream *Open(const char *url, Mutex &mutex, Cond &cond, + Error &error); + + bool IsEOF() const { + return eof; + } + + size_t Read(void *ptr, size_t size, Error &error); + + Tag *ReadTag() { + Tag *result = tag; + tag = nullptr; + return result; + } + + void Callback(int sig); + +private: + void FillBuffer(); }; -static void -refill_buffer(DespotifyInputStream *ctx) +inline void +DespotifyInputStream::FillBuffer() { /* Wait until there is data */ while (1) { - int rc = despotify_get_pcm(ctx->session, &ctx->pcm); + int rc = despotify_get_pcm(session, &pcm); - if (rc == 0 && ctx->pcm.len) { - ctx->len_available = ctx->pcm.len; + if (rc == 0 && pcm.len) { + len_available = pcm.len; break; } - if (ctx->eof == true) + + if (eof == true) break; if (rc < 0) { LogDebug(despotify_domain, "despotify_get_pcm error"); - ctx->eof = true; + eof = true; break; } @@ -95,11 +116,9 @@ refill_buffer(DespotifyInputStream *ctx) } } -static void callback(gcc_unused struct despotify_session* ds, - int sig, gcc_unused void* data, void* callback_data) +inline void +DespotifyInputStream::Callback(int sig) { - DespotifyInputStream *ctx = (DespotifyInputStream *)callback_data; - switch (sig) { case DESPOTIFY_NEW_TRACK: break; @@ -109,35 +128,38 @@ static void callback(gcc_unused struct despotify_session* ds, case DESPOTIFY_TRACK_PLAY_ERROR: LogWarning(despotify_domain, "Track play error"); - ctx->eof = true; - ctx->len_available = 0; + eof = true; + len_available = 0; break; case DESPOTIFY_END_OF_PLAYLIST: - ctx->eof = true; - FormatDebug(despotify_domain, "End of playlist: %d", ctx->eof); + eof = true; + LogDebug(despotify_domain, "End of playlist"); break; } } - -static InputStream * -input_despotify_open(const char *url, - Mutex &mutex, Cond &cond, - gcc_unused Error &error) +static void callback(gcc_unused struct despotify_session* ds, + int sig, gcc_unused void* data, void* callback_data) { - struct despotify_session *session; - struct ds_link *ds_link; - struct ds_track *track; + DespotifyInputStream *ctx = (DespotifyInputStream *)callback_data; - if (!g_str_has_prefix(url, "spt://")) + ctx->Callback(sig); +} + +inline InputStream * +DespotifyInputStream::Open(const char *url, + Mutex &mutex, Cond &cond, + gcc_unused Error &error) +{ + if (!StringStartsWith(url, "spt://")) return nullptr; - session = mpd_despotify_get_session(); - if (!session) + despotify_session *session = mpd_despotify_get_session(); + if (session == nullptr) return nullptr; - ds_link = despotify_link_from_uri(url + 6); + ds_link *ds_link = despotify_link_from_uri(url + 6); if (!ds_link) { FormatDebug(despotify_domain, "Can't find %s", url); return nullptr; @@ -147,7 +169,7 @@ input_despotify_open(const char *url, return nullptr; } - track = despotify_link_get_track(session, ds_link); + ds_track *track = despotify_link_get_track(session, ds_link); despotify_free_link(ds_link); if (!track) return nullptr; @@ -170,26 +192,34 @@ input_despotify_open(const char *url, return &ctx->base; } -static size_t -input_despotify_read(InputStream *is, void *ptr, size_t size, - gcc_unused Error &error) +static InputStream * +input_despotify_open(const char *url, Mutex &mutex, Cond &cond, Error &error) { - DespotifyInputStream *ctx = (DespotifyInputStream *)is; - size_t to_cpy = size; + return DespotifyInputStream::Open(url, mutex, cond, error); +} - if (ctx->len_available == 0) - refill_buffer(ctx); +inline size_t +DespotifyInputStream::Read(void *ptr, size_t size, gcc_unused Error &error) +{ + if (len_available == 0) + FillBuffer(); - if (ctx->len_available < size) - to_cpy = ctx->len_available; - memcpy(ptr, ctx->pcm.buf, to_cpy); - ctx->len_available -= to_cpy; + size_t to_cpy = std::min(size, len_available); + memcpy(ptr, pcm.buf, to_cpy); + len_available -= to_cpy; - is->offset += to_cpy; + base.offset += to_cpy; return to_cpy; } +static size_t +input_despotify_read(InputStream *is, void *ptr, size_t size, Error &error) +{ + DespotifyInputStream *ctx = (DespotifyInputStream *)is; + return ctx->Read(ptr, size, error); +} + static void input_despotify_close(InputStream *is) { @@ -204,18 +234,15 @@ input_despotify_eof(InputStream *is) { DespotifyInputStream *ctx = (DespotifyInputStream *)is; - return ctx->eof; + return ctx->IsEOF(); } static Tag * input_despotify_tag(InputStream *is) { DespotifyInputStream *ctx = (DespotifyInputStream *)is; - Tag *tag = ctx->tag; - - ctx->tag = nullptr; - return tag; + return ctx->ReadTag(); } const InputPlugin input_plugin_despotify = { diff --git a/src/input/FfmpegInputPlugin.cxx b/src/input/FfmpegInputPlugin.cxx index 8f9cd0b86..7d041677b 100644 --- a/src/input/FfmpegInputPlugin.cxx +++ b/src/input/FfmpegInputPlugin.cxx @@ -24,17 +24,15 @@ #include "FfmpegInputPlugin.hxx" #include "InputStream.hxx" #include "InputPlugin.hxx" +#include "util/StringUtil.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" extern "C" { -#include <libavutil/avutil.h> #include <libavformat/avio.h> #include <libavformat/avformat.h> } -#include <glib.h> - struct FfmpegInputStream { InputStream base; @@ -91,12 +89,12 @@ input_ffmpeg_open(const char *uri, Mutex &mutex, Cond &cond, Error &error) { - if (!g_str_has_prefix(uri, "gopher://") && - !g_str_has_prefix(uri, "rtp://") && - !g_str_has_prefix(uri, "rtsp://") && - !g_str_has_prefix(uri, "rtmp://") && - !g_str_has_prefix(uri, "rtmpt://") && - !g_str_has_prefix(uri, "rtmps://")) + if (!StringStartsWith(uri, "gopher://") && + !StringStartsWith(uri, "rtp://") && + !StringStartsWith(uri, "rtsp://") && + !StringStartsWith(uri, "rtmp://") && + !StringStartsWith(uri, "rtmpt://") && + !StringStartsWith(uri, "rtmps://")) return nullptr; AVIOContext *h; diff --git a/src/input/FileInputPlugin.cxx b/src/input/FileInputPlugin.cxx index 26e40d609..5a63a469c 100644 --- a/src/input/FileInputPlugin.cxx +++ b/src/input/FileInputPlugin.cxx @@ -30,8 +30,6 @@ #include <sys/stat.h> #include <unistd.h> #include <errno.h> -#include <string.h> -#include <glib.h> static constexpr Domain file_domain("file"); @@ -62,7 +60,7 @@ input_file_open(const char *filename, int fd, ret; struct stat st; - if (!PathTraits::IsAbsoluteFS(filename)) + if (!PathTraitsFS::IsAbsolute(filename)) return nullptr; fd = open_cloexec(filename, O_RDONLY|O_BINARY, 0); diff --git a/src/input/MmsInputPlugin.cxx b/src/input/MmsInputPlugin.cxx index e97c1eb3f..2c7f6d166 100644 --- a/src/input/MmsInputPlugin.cxx +++ b/src/input/MmsInputPlugin.cxx @@ -21,15 +21,12 @@ #include "MmsInputPlugin.hxx" #include "InputStream.hxx" #include "InputPlugin.hxx" +#include "util/StringUtil.hxx" #include "util/Error.hxx" #include "util/Domain.hxx" -#include <glib.h> #include <libmms/mmsx.h> -#include <string.h> -#include <errno.h> - struct MmsInputStream { InputStream base; @@ -61,10 +58,10 @@ input_mms_open(const char *url, Mutex &mutex, Cond &cond, Error &error) { - if (!g_str_has_prefix(url, "mms://") && - !g_str_has_prefix(url, "mmsh://") && - !g_str_has_prefix(url, "mmst://") && - !g_str_has_prefix(url, "mmsu://")) + if (!StringStartsWith(url, "mms://") && + !StringStartsWith(url, "mmsh://") && + !StringStartsWith(url, "mmst://") && + !StringStartsWith(url, "mmsu://")) return nullptr; const auto mms = mmsx_connect(nullptr, nullptr, url, 128 * 1024); diff --git a/src/input/RewindInputPlugin.cxx b/src/input/RewindInputPlugin.cxx index e11f56631..78ab75660 100644 --- a/src/input/RewindInputPlugin.cxx +++ b/src/input/RewindInputPlugin.cxx @@ -21,7 +21,6 @@ #include "RewindInputPlugin.hxx" #include "InputStream.hxx" #include "InputPlugin.hxx" -#include "tag/Tag.hxx" #include <assert.h> #include <string.h> |