Commit 2835998a authored by Alexey Rusakov's avatar Alexey Rusakov

Merge commit 'release-0.15.8' into alt

parents eaff52fa 760569fc
...@@ -75,6 +75,7 @@ mpd_headers = \ ...@@ -75,6 +75,7 @@ mpd_headers = \
src/input_stream.h \ src/input_stream.h \
src/input/file_input_plugin.h \ src/input/file_input_plugin.h \
src/input/curl_input_plugin.h \ src/input/curl_input_plugin.h \
src/input/rewind_input_plugin.h \
src/input/lastfm_input_plugin.h \ src/input/lastfm_input_plugin.h \
src/input/mms_input_plugin.h \ src/input/mms_input_plugin.h \
src/icy_server.h \ src/icy_server.h \
...@@ -467,7 +468,9 @@ INPUT_SRC = \ ...@@ -467,7 +468,9 @@ INPUT_SRC = \
src/input/file_input_plugin.c src/input/file_input_plugin.c
if HAVE_CURL if HAVE_CURL
INPUT_SRC += src/input/curl_input_plugin.c src/icy_metadata.c INPUT_SRC += src/input/curl_input_plugin.c \
src/input/rewind_input_plugin.c \
src/icy_metadata.c
endif endif
if ENABLE_LASTFM if ENABLE_LASTFM
...@@ -637,6 +640,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \ ...@@ -637,6 +640,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \
src/tag.c src/tag_pool.c \ src/tag.c src/tag_pool.c \
src/replay_gain.c \ src/replay_gain.c \
src/uri.c \ src/uri.c \
src/timer.c \
$(ARCHIVE_SRC) \ $(ARCHIVE_SRC) \
$(INPUT_SRC) \ $(INPUT_SRC) \
$(TAG_SRC) \ $(TAG_SRC) \
...@@ -656,6 +660,7 @@ test_read_tags_SOURCES = test/read_tags.c \ ...@@ -656,6 +660,7 @@ test_read_tags_SOURCES = test/read_tags.c \
src/tag.c src/tag_pool.c \ src/tag.c src/tag_pool.c \
src/replay_gain.c \ src/replay_gain.c \
src/uri.c \ src/uri.c \
src/timer.c \
$(ARCHIVE_SRC) \ $(ARCHIVE_SRC) \
$(INPUT_SRC) \ $(INPUT_SRC) \
$(TAG_SRC) \ $(TAG_SRC) \
...@@ -748,6 +753,7 @@ DOCBOOK_HTML = ...@@ -748,6 +753,7 @@ DOCBOOK_HTML =
endif endif
doc/api/html/index.html: doc/doxygen.conf doc/api/html/index.html: doc/doxygen.conf
@mkdir -p $(@D)
$(DOXYGEN) $< $(DOXYGEN) $<
all-local: $(DOCBOOK_HTML) doc/api/html/index.html all-local: $(DOCBOOK_HTML) doc/api/html/index.html
......
ver 0.15.8 (2010/01/17)
* input:
- curl: allow rewinding with Icy-Metadata
* decoders:
- ffmpeg, flac, vorbis: added more flac/vorbis MIME types
- ffmpeg: enabled libavformat's file name extension detection
* dbUtils: return empty tag value only if no value was found
* decoder_thread: fix CUE track playback
* queue: don't repeat current song in consume mode
ver 0.15.7 (2009/12/27)
* archive:
- close archive when stream is closed
- iso, zip: fixed memory leak in destructor
* input:
- file: don't fall back to parent directory
- archive: fixed memory leak in error handler
* tags:
- id3: fix ID3v1 charset conversion
* decoders:
- eliminate jitter after seek failure
- ffmpeg: don't try to force stereo
- wavpack: allow fine-grained seeking
* mixer: explicitly close all mixers on shutdown
* mapper: fix memory leak when playlist_directory is not set
* mapper: apply filesystem_charset to playlists
* command: verify playlist name in the "rm" command
* database: return multiple tag values per song
ver 0.15.6 (2009/11/18)
* input:
- lastfm: fixed variable name in GLib<2.16 code path
- input/mms: require libmms 0.4
* archive:
- zzip: require libzzip 0.13
* tags:
- id3: allow 4 MB RIFF/AIFF tags
* decoders:
- ffmpeg: convert metadata
- ffmpeg: align the output buffer
- oggflac: rewind stream after FLAC detection
- flac: fixed CUE seeking range check
- flac: fixed NULL pointer dereference in CUE code
* output_thread: check again if output is open on PAUSE
* update: delete ignored symlinks from database
* database: increased maximum line length to 32 kB
* sticker: added fallback for sqlite3_prepare_v2()
ver 0.15.5 (2009/10/18) ver 0.15.5 (2009/10/18)
* input: * input:
- curl: don't abort if a packet has only metadata - curl: don't abort if a packet has only metadata
......
AC_PREREQ(2.60) AC_PREREQ(2.60)
AC_INIT(mpd, 0.15.5, musicpd-dev-team@lists.sourceforge.net) AC_INIT(mpd, 0.15.8, musicpd-dev-team@lists.sourceforge.net)
AC_CONFIG_SRCDIR([src/main.c]) AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2]) AM_INIT_AUTOMAKE([foreign 1.9 dist-bzip2])
AM_CONFIG_HEADER(config.h) AM_CONFIG_HEADER(config.h)
...@@ -303,7 +303,7 @@ AC_ARG_ENABLE(mms, ...@@ -303,7 +303,7 @@ AC_ARG_ENABLE(mms,
[enable the MMS protocol with libmms]),, [enable the MMS protocol with libmms]),,
[enable_mms=auto]) [enable_mms=auto])
MPD_AUTO_PKG(mms, MMS, [libmms], MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4],
[libmms mms:// protocol support], [libmms not found]) [libmms mms:// protocol support], [libmms not found])
if test x$enable_mms = xyes; then if test x$enable_mms = xyes; then
AC_DEFINE(ENABLE_MMS, 1, AC_DEFINE(ENABLE_MMS, 1,
...@@ -339,7 +339,7 @@ AC_ARG_ENABLE(zip, ...@@ -339,7 +339,7 @@ AC_ARG_ENABLE(zip,
[enable zip archive support (default: disabled)]),, [enable zip archive support (default: disabled)]),,
enable_zip=no) enable_zip=no)
MPD_AUTO_PKG(zip, ZZIP, [zziplib], MPD_AUTO_PKG(zip, ZZIP, [zziplib >= 0.13],
[libzzip archive library], [libzzip not found]) [libzzip archive library], [libzzip not found])
AM_CONDITIONAL(HAVE_ZIP, test x$enable_zip = xyes) AM_CONDITIONAL(HAVE_ZIP, test x$enable_zip = xyes)
...@@ -1139,7 +1139,6 @@ then ...@@ -1139,7 +1139,6 @@ then
MPD_CHECK_FLAG([-Wextra]) MPD_CHECK_FLAG([-Wextra])
MPD_CHECK_FLAG([-Wno-deprecated-declarations]) MPD_CHECK_FLAG([-Wno-deprecated-declarations])
MPD_CHECK_FLAG([-Wmissing-prototypes]) MPD_CHECK_FLAG([-Wmissing-prototypes])
MPD_CHECK_FLAG([-Wdeclaration-after-statement])
MPD_CHECK_FLAG([-Wshadow]) MPD_CHECK_FLAG([-Wshadow])
MPD_CHECK_FLAG([-Wpointer-arith]) MPD_CHECK_FLAG([-Wpointer-arith])
MPD_CHECK_FLAG([-Wstrict-prototypes]) MPD_CHECK_FLAG([-Wstrict-prototypes])
......
...@@ -140,8 +140,8 @@ static void ...@@ -140,8 +140,8 @@ static void
bz2_close(struct archive_file *file) bz2_close(struct archive_file *file)
{ {
bz2_context *context = (bz2_context *) file; bz2_context *context = (bz2_context *) file;
if (context->name)
g_free(context->name); g_free(context->name);
input_stream_close(&context->istream); input_stream_close(&context->istream);
g_free(context); g_free(context);
...@@ -173,6 +173,8 @@ bz2_is_close(struct input_stream *is) ...@@ -173,6 +173,8 @@ bz2_is_close(struct input_stream *is)
bz2_context *context = (bz2_context *) is->data; bz2_context *context = (bz2_context *) is->data;
bz2_destroy(context); bz2_destroy(context);
is->data = NULL; is->data = NULL;
bz2_close((struct archive_file *)context);
} }
static int static int
......
...@@ -132,7 +132,8 @@ iso_close(struct archive_file *file) ...@@ -132,7 +132,8 @@ iso_close(struct archive_file *file)
} }
//close archive //close archive
iso9660_close(context->iso); iso9660_close(context->iso);
context->iso = NULL;
g_free(context);
} }
/* single archive handling */ /* single archive handling */
...@@ -165,6 +166,8 @@ iso_is_close(struct input_stream *is) ...@@ -165,6 +166,8 @@ iso_is_close(struct input_stream *is)
{ {
iso_context *context = (iso_context *) is->data; iso_context *context = (iso_context *) is->data;
g_free(context->statbuf); g_free(context->statbuf);
iso_close((struct archive_file *)context);
} }
......
...@@ -99,7 +99,8 @@ zip_close(struct archive_file *file) ...@@ -99,7 +99,8 @@ zip_close(struct archive_file *file)
} }
//close archive //close archive
zzip_dir_close (context->dir); zzip_dir_close (context->dir);
context->dir = NULL;
g_free(context);
} }
/* single archive handling */ /* single archive handling */
...@@ -133,6 +134,8 @@ zip_is_close(struct input_stream *is) ...@@ -133,6 +134,8 @@ zip_is_close(struct input_stream *is)
{ {
zip_context *context = (zip_context *) is->data; zip_context *context = (zip_context *) is->data;
zzip_file_close (context->file); zzip_file_close (context->file);
zip_close((struct archive_file *)context);
} }
static size_t static size_t
......
...@@ -73,6 +73,9 @@ struct archive_plugin { ...@@ -73,6 +73,9 @@ struct archive_plugin {
/** /**
* Opens an input_stream of a file within the archive. * Opens an input_stream of a file within the archive.
* *
* If this function succeeds, then the #input_stream "owns"
* the archive file and will automatically close it.
*
* @param path the path within the archive * @param path the path within the archive
*/ */
bool (*open_stream)(struct archive_file *, struct input_stream *is, bool (*open_stream)(struct archive_file *, struct input_stream *is,
......
...@@ -234,6 +234,7 @@ visitTag(struct client *client, struct strset *set, ...@@ -234,6 +234,7 @@ visitTag(struct client *client, struct strset *set,
struct song *song, enum tag_type tagType) struct song *song, enum tag_type tagType)
{ {
struct tag *tag = song->tag; struct tag *tag = song->tag;
bool found = false;
if (tagType == LOCATE_TAG_FILE_TYPE) { if (tagType == LOCATE_TAG_FILE_TYPE) {
song_print_url(client, song); song_print_url(client, song);
...@@ -246,11 +247,12 @@ visitTag(struct client *client, struct strset *set, ...@@ -246,11 +247,12 @@ visitTag(struct client *client, struct strset *set,
for (unsigned i = 0; i < tag->num_items; i++) { for (unsigned i = 0; i < tag->num_items; i++) {
if (tag->items[i]->type == tagType) { if (tag->items[i]->type == tagType) {
strset_add(set, tag->items[i]->value); strset_add(set, tag->items[i]->value);
return; found = true;
} }
} }
strset_add(set, ""); if (!found)
strset_add(set, "");
} }
struct list_tags_data { struct list_tags_data {
......
...@@ -415,11 +415,11 @@ flac_vtrack_tnum(const char* fname) ...@@ -415,11 +415,11 @@ flac_vtrack_tnum(const char* fname)
* another/better way would be to use tag struct * another/better way would be to use tag struct
*/ */
char* ptr = strrchr(fname, '_'); char* ptr = strrchr(fname, '_');
if (ptr == NULL)
return 0;
// copy ascii tracknumber to int // copy ascii tracknumber to int
char vtrack[4]; return (unsigned int)strtol(++ptr, NULL, 10);
g_strlcpy(vtrack, ++ptr, 4);
return (unsigned int)strtol(vtrack, NULL, 10);
} }
#endif /* FLAC_API_VERSION_CURRENT >= 7 */ #endif /* FLAC_API_VERSION_CURRENT >= 7 */
...@@ -55,7 +55,7 @@ struct ffmpeg_context { ...@@ -55,7 +55,7 @@ struct ffmpeg_context {
struct ffmpeg_stream { struct ffmpeg_stream {
/** hack - see url_to_struct() */ /** hack - see url_to_struct() */
char url[8]; char url[64];
struct decoder *decoder; struct decoder *decoder;
struct input_stream *input; struct input_stream *input;
...@@ -140,8 +140,28 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context) ...@@ -140,8 +140,28 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
return -1; return -1;
} }
/**
* Append the suffix of the original URI to the virtual stream URI.
* Without this, libavformat cannot detect some of the codecs
* (e.g. "shorten").
*/
static void
append_uri_suffix(struct ffmpeg_stream *stream, const char *uri)
{
assert(stream != NULL);
assert(uri != NULL);
char *base = g_path_get_basename(uri);
const char *suffix = strrchr(base, '.');
if (suffix != NULL && suffix[1] != 0)
g_strlcat(stream->url, suffix, sizeof(stream->url));
g_free(base);
}
static bool static bool
ffmpeg_helper(struct input_stream *input, ffmpeg_helper(const char *uri, struct input_stream *input,
bool (*callback)(struct ffmpeg_context *ctx), bool (*callback)(struct ffmpeg_context *ctx),
struct ffmpeg_context *ctx) struct ffmpeg_context *ctx)
{ {
...@@ -154,6 +174,9 @@ ffmpeg_helper(struct input_stream *input, ...@@ -154,6 +174,9 @@ ffmpeg_helper(struct input_stream *input,
}; };
bool ret; bool ret;
if (uri != NULL)
append_uri_suffix(&stream, uri);
stream.input = input; stream.input = input;
if (ctx && ctx->decoder) { if (ctx && ctx->decoder) {
stream.decoder = ctx->decoder; //are we in decoding loop ? stream.decoder = ctx->decoder; //are we in decoding loop ?
...@@ -209,6 +232,21 @@ ffmpeg_helper(struct input_stream *input, ...@@ -209,6 +232,21 @@ ffmpeg_helper(struct input_stream *input,
return ret; return ret;
} }
/**
* On some platforms, libavcodec wants the output buffer aligned to 16
* bytes (because it uses SSE/Altivec internally). This function
* returns the aligned version of the specified buffer, and corrects
* the buffer size.
*/
static void *
align16(void *p, size_t *length_p)
{
unsigned add = 16 - (size_t)p % 16;
*length_p -= add;
return (char *)p + add;
}
static enum decoder_command static enum decoder_command
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is, ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
const AVPacket *packet, const AVPacket *packet,
...@@ -217,7 +255,9 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is, ...@@ -217,7 +255,9 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
{ {
enum decoder_command cmd = DECODE_COMMAND_NONE; enum decoder_command cmd = DECODE_COMMAND_NONE;
int position; int position;
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2]; uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
int16_t *aligned_buffer;
size_t buffer_size;
int len, audio_size; int len, audio_size;
uint8_t *packet_data; uint8_t *packet_data;
int packet_size; int packet_size;
...@@ -225,11 +265,13 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is, ...@@ -225,11 +265,13 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
packet_data = packet->data; packet_data = packet->data;
packet_size = packet->size; packet_size = packet->size;
buffer_size = sizeof(audio_buf);
aligned_buffer = align16(audio_buf, &buffer_size);
while ((packet_size > 0) && (cmd == DECODE_COMMAND_NONE)) { while ((packet_size > 0) && (cmd == DECODE_COMMAND_NONE)) {
audio_size = sizeof(audio_buf); audio_size = buffer_size;
len = avcodec_decode_audio2(codec_context, len = avcodec_decode_audio2(codec_context,
(int16_t *)audio_buf, aligned_buffer, &audio_size,
&audio_size,
packet_data, packet_size); packet_data, packet_size);
if (len < 0) { if (len < 0) {
...@@ -250,7 +292,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is, ...@@ -250,7 +292,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
: 0; : 0;
cmd = decoder_data(decoder, is, cmd = decoder_data(decoder, is,
audio_buf, audio_size, aligned_buffer, audio_size,
position, position,
codec_context->bit_rate / 1000, NULL); codec_context->bit_rate / 1000, NULL);
} }
...@@ -270,10 +312,6 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx) ...@@ -270,10 +312,6 @@ ffmpeg_decode_internal(struct ffmpeg_context *ctx)
total_time = 0; total_time = 0;
if (codec_context->channels > 2) {
codec_context->channels = 2;
}
#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0) #if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
audio_format.bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt); audio_format.bits = (uint8_t) av_get_bits_per_sample_format(codec_context->sample_fmt);
#else #else
...@@ -334,7 +372,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input) ...@@ -334,7 +372,8 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
ctx.input = input; ctx.input = input;
ctx.decoder = decoder; ctx.decoder = decoder;
ffmpeg_helper(input, ffmpeg_decode_internal, &ctx); ffmpeg_helper(decoder_get_uri(decoder), input,
ffmpeg_decode_internal, &ctx);
} }
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0) #if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
...@@ -352,17 +391,17 @@ ffmpeg_copy_metadata(struct tag *tag, AVMetadata *m, ...@@ -352,17 +391,17 @@ ffmpeg_copy_metadata(struct tag *tag, AVMetadata *m,
static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx) static bool ffmpeg_tag_internal(struct ffmpeg_context *ctx)
{ {
struct tag *tag = (struct tag *) ctx->tag; struct tag *tag = (struct tag *) ctx->tag;
const AVFormatContext *f = ctx->format_context; AVFormatContext *f = ctx->format_context;
tag->time = 0; tag->time = 0;
if (f->duration != (int64_t)AV_NOPTS_VALUE) if (f->duration != (int64_t)AV_NOPTS_VALUE)
tag->time = f->duration / AV_TIME_BASE; tag->time = f->duration / AV_TIME_BASE;
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0) #if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TITLE, "title"); ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_TITLE, "title");
if (!ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "author")) ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ARTIST, "author");
ffmpeg_copy_metadata(tag, f->metadata,
TAG_ITEM_ARTIST, "artist");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ALBUM, "album"); ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_ALBUM, "album");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_COMMENT, "comment"); ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_COMMENT, "comment");
ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_GENRE, "genre"); ffmpeg_copy_metadata(tag, f->metadata, TAG_ITEM_GENRE, "genre");
...@@ -411,7 +450,7 @@ static struct tag *ffmpeg_tag(const char *file) ...@@ -411,7 +450,7 @@ static struct tag *ffmpeg_tag(const char *file)
ctx.decoder = NULL; ctx.decoder = NULL;
ctx.tag = tag_new(); ctx.tag = tag_new();
ret = ffmpeg_helper(&input, ffmpeg_tag_internal, &ctx); ret = ffmpeg_helper(file, &input, ffmpeg_tag_internal, &ctx);
if (!ret) { if (!ret) {
tag_free(ctx.tag); tag_free(ctx.tag);
ctx.tag = NULL; ctx.tag = NULL;
...@@ -447,25 +486,31 @@ static const char *const ffmpeg_suffixes[] = { ...@@ -447,25 +486,31 @@ static const char *const ffmpeg_suffixes[] = {
}; };
static const char *const ffmpeg_mime_types[] = { static const char *const ffmpeg_mime_types[] = {
"application/m4a",
"application/mp4", "application/mp4",
"application/octet-stream", "application/octet-stream",
"application/ogg", "application/ogg",
"application/x-ms-wmz", "application/x-ms-wmz",
"application/x-ms-wmd", "application/x-ms-wmd",
"application/x-ogg",
"application/x-shockwave-flash", "application/x-shockwave-flash",
"application/x-shorten", "application/x-shorten",
"audio/8svx", "audio/8svx",
"audio/16sv", "audio/16sv",
"audio/aac", "audio/aac",
"audio/ac3", "audio/ac3",
"audio/aiff"
"audio/amr", "audio/amr",
"audio/basic", "audio/basic",
"audio/flac", "audio/flac",
"audio/m4a",
"audio/mp4",
"audio/mpeg", "audio/mpeg",
"audio/musepack", "audio/musepack",
"audio/ogg", "audio/ogg",
"audio/qcelp", "audio/qcelp",
"audio/vorbis", "audio/vorbis",
"audio/vorbis+ogg",
"audio/x-8svx", "audio/x-8svx",
"audio/x-16sv", "audio/x-16sv",
"audio/x-aac", "audio/x-aac",
...@@ -478,15 +523,20 @@ static const char *const ffmpeg_mime_types[] = { ...@@ -478,15 +523,20 @@ static const char *const ffmpeg_mime_types[] = {
"audio/x-flac", "audio/x-flac",
"audio/x-gsm", "audio/x-gsm",
"audio/x-mace", "audio/x-mace",
"audio/x-matroska",
"audio/x-monkeys-audio", "audio/x-monkeys-audio",
"audio/x-mpeg", "audio/x-mpeg",
"audio/x-ms-wma", "audio/x-ms-wma",
"audio/x-ms-wax", "audio/x-ms-wax",
"audio/x-musepack", "audio/x-musepack",
"audio/x-ogg",
"audio/x-vorbis",
"audio/x-vorbis+ogg",
"audio/x-pn-realaudio", "audio/x-pn-realaudio",
"audio/x-pn-multirate-realaudio", "audio/x-pn-multirate-realaudio",
"audio/x-speex", "audio/x-speex",
"audio/x-tta" "audio/x-tta"
"audio/x-voc",
"audio/x-wav", "audio/x-wav",
"audio/x-wma", "audio/x-wma",
"audio/x-wv", "audio/x-wv",
......
...@@ -629,21 +629,15 @@ flac_container_decode(struct decoder* decoder, ...@@ -629,21 +629,15 @@ flac_container_decode(struct decoder* decoder,
FLAC__uint64 seek_sample = t_start + FLAC__uint64 seek_sample = t_start +
(decoder_seek_where(decoder) * data.audio_format.sample_rate); (decoder_seek_where(decoder) * data.audio_format.sample_rate);
//if (seek_sample >= t_start && seek_sample <= t_end && data.total_time > 30) if (seek_sample >= t_start && seek_sample <= t_end &&
if (seek_sample >= t_start && seek_sample <= t_end) flac_seek_absolute(flac_dec, (FLAC__uint64)seek_sample)) {
{ data.time = (float)(seek_sample - t_start) /
if (flac_seek_absolute(flac_dec, (FLAC__uint64)seek_sample)) data.audio_format.sample_rate;
{ data.position = 0;
data.time = (float)(seek_sample - t_start) /
data.audio_format.sample_rate;
data.position = 0;
decoder_command_finished(decoder); decoder_command_finished(decoder);
} } else
else decoder_seek_error(decoder);
decoder_seek_error(decoder);
//decoder_command_finished(decoder);
}
} }
else if (flac_get_state(flac_dec) == flac_decoder_eof) else if (flac_get_state(flac_dec) == flac_decoder_eof)
break; break;
...@@ -877,9 +871,11 @@ oggflac_decode(struct decoder *decoder, struct input_stream *input_stream) ...@@ -877,9 +871,11 @@ oggflac_decode(struct decoder *decoder, struct input_stream *input_stream)
static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL }; static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
static const char *const oggflac_mime_types[] = { static const char *const oggflac_mime_types[] = {
"audio/x-flac+ogg",
"application/ogg", "application/ogg",
"application/x-ogg", "application/x-ogg",
"audio/ogg",
"audio/x-flac+ogg",
"audio/x-ogg",
NULL NULL
}; };
...@@ -900,7 +896,11 @@ const struct decoder_plugin oggflac_decoder_plugin = { ...@@ -900,7 +896,11 @@ const struct decoder_plugin oggflac_decoder_plugin = {
static const char *const flac_suffixes[] = { "flac", NULL }; static const char *const flac_suffixes[] = { "flac", NULL };
static const char *const flac_mime_types[] = { static const char *const flac_mime_types[] = {
"audio/x-flac", "application/x-flac", NULL "application/flac",
"application/x-flac",
"audio/flac",
"audio/x-flac",
NULL
}; };
const struct decoder_plugin flac_decoder_plugin = { const struct decoder_plugin flac_decoder_plugin = {
......
...@@ -272,6 +272,10 @@ oggflac_tag_dup(const char *file) ...@@ -272,6 +272,10 @@ oggflac_tag_dup(const char *file)
return NULL; return NULL;
} }
/* rewind the stream, because ogg_stream_type_detect() has
moved it */
input_stream_seek(&input_stream, 0, SEEK_SET);
flac_data_init(&data, NULL, &input_stream); flac_data_init(&data, NULL, &input_stream);
data.tag = tag_new(); data.tag = tag_new();
...@@ -300,6 +304,10 @@ oggflac_decode(struct decoder * mpd_decoder, struct input_stream *input_stream) ...@@ -300,6 +304,10 @@ oggflac_decode(struct decoder * mpd_decoder, struct input_stream *input_stream)
if (ogg_stream_type_detect(input_stream) != FLAC) if (ogg_stream_type_detect(input_stream) != FLAC)
return; return;
/* rewind the stream, because ogg_stream_type_detect() has
moved it */
input_stream_seek(input_stream, 0, SEEK_SET);
flac_data_init(&data, mpd_decoder, input_stream); flac_data_init(&data, mpd_decoder, input_stream);
if (!(decoder = full_decoder_init_and_read_metadata(&data, 0))) { if (!(decoder = full_decoder_init_and_read_metadata(&data, 0))) {
...@@ -349,9 +357,11 @@ fail: ...@@ -349,9 +357,11 @@ fail:
static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL }; static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
static const char *const oggflac_mime_types[] = { static const char *const oggflac_mime_types[] = {
"audio/x-flac+ogg",
"application/ogg", "application/ogg",
"application/x-ogg", "application/x-ogg",
"audio/ogg",
"audio/x-ogg",
"audio/x-flac+ogg",
NULL NULL
}; };
......
...@@ -405,8 +405,13 @@ static const char *const vorbis_suffixes[] = { ...@@ -405,8 +405,13 @@ static const char *const vorbis_suffixes[] = {
static const char *const vorbis_mime_types[] = { static const char *const vorbis_mime_types[] = {
"application/ogg", "application/ogg",
"audio/x-vorbis+ogg",
"application/x-ogg", "application/x-ogg",
"audio/ogg",
"audio/vorbis",
"audio/vorbis+ogg",
"audio/x-ogg",
"audio/x-vorbis",
"audio/x-vorbis+ogg",
NULL NULL
}; };
......
...@@ -72,7 +72,7 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count) ...@@ -72,7 +72,7 @@ format_samples_int(int bytes_per_sample, void *buffer, uint32_t count)
switch (bytes_per_sample) { switch (bytes_per_sample) {
case 1: { case 1: {
uchar *dst = buffer; int8_t *dst = buffer;
/* /*
* The asserts like the following one are because we do the * The asserts like the following one are because we do the
* formatting of samples within a single buffer. The size * formatting of samples within a single buffer. The size
...@@ -185,10 +185,9 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek, ...@@ -185,10 +185,9 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek,
do { do {
if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) { if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) {
if (can_seek) { if (can_seek) {
int where; unsigned where = decoder_seek_where(decoder) *
audio_format.sample_rate;
where = decoder_seek_where(decoder);
where *= audio_format.sample_rate;
if (WavpackSeekSample(wpc, where)) { if (WavpackSeekSample(wpc, where)) {
position = where; position = where;
decoder_command_finished(decoder); decoder_command_finished(decoder);
......
...@@ -93,7 +93,9 @@ void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder) ...@@ -93,7 +93,9 @@ void decoder_command_finished(G_GNUC_UNUSED struct decoder * decoder)
dc.seek_error || decoder->seeking); dc.seek_error || decoder->seeking);
assert(dc.pipe != NULL); assert(dc.pipe != NULL);
if (dc.command == DECODE_COMMAND_SEEK) { if (decoder->seeking) {
decoder->seeking = false;
/* delete frames from the old song position */ /* delete frames from the old song position */
if (decoder->chunk != NULL) { if (decoder->chunk != NULL) {
...@@ -124,6 +126,8 @@ void decoder_seek_error(struct decoder * decoder) ...@@ -124,6 +126,8 @@ void decoder_seek_error(struct decoder * decoder)
assert(dc.pipe != NULL); assert(dc.pipe != NULL);
dc.seek_error = true; dc.seek_error = true;
decoder->seeking = false;
decoder_command_finished(decoder); decoder_command_finished(decoder);
} }
......
...@@ -26,9 +26,6 @@ ...@@ -26,9 +26,6 @@
#include <assert.h> #include <assert.h>
#define DECODE_TYPE_FILE 0
#define DECODE_TYPE_URL 1
enum decoder_state { enum decoder_state {
DECODE_STATE_STOP = 0, DECODE_STATE_STOP = 0,
DECODE_STATE_START, DECODE_STATE_START,
......
...@@ -89,7 +89,8 @@ static void decoder_run_song(const struct song *song, const char *uri) ...@@ -89,7 +89,8 @@ static void decoder_run_song(const struct song *song, const char *uri)
struct input_stream input_stream; struct input_stream input_stream;
const struct decoder_plugin *plugin; const struct decoder_plugin *plugin;
if (!input_stream_open(&input_stream, uri)) { close_instream = input_stream_open(&input_stream, uri);
if (!close_instream && !song_is_file(song)) {
dc.state = DECODE_STATE_ERROR; dc.state = DECODE_STATE_ERROR;
return; return;
} }
...@@ -108,7 +109,7 @@ static void decoder_run_song(const struct song *song, const char *uri) ...@@ -108,7 +109,7 @@ static void decoder_run_song(const struct song *song, const char *uri)
/* wait for the input stream to become ready; its metadata /* wait for the input stream to become ready; its metadata
will be available then */ will be available then */
while (!input_stream.ready) { while (close_instream && !input_stream.ready) {
if (dc.command == DECODE_COMMAND_STOP) { if (dc.command == DECODE_COMMAND_STOP) {
input_stream_close(&input_stream); input_stream_close(&input_stream);
dc.state = DECODE_STATE_STOP; dc.state = DECODE_STATE_STOP;
...@@ -124,7 +125,8 @@ static void decoder_run_song(const struct song *song, const char *uri) ...@@ -124,7 +125,8 @@ static void decoder_run_song(const struct song *song, const char *uri)
} }
if (dc.command == DECODE_COMMAND_STOP) { if (dc.command == DECODE_COMMAND_STOP) {
input_stream_close(&input_stream); if (close_instream)
input_stream_close(&input_stream);
dc.state = DECODE_STATE_STOP; dc.state = DECODE_STATE_STOP;
return; return;
} }
...@@ -179,8 +181,11 @@ static void decoder_run_song(const struct song *song, const char *uri) ...@@ -179,8 +181,11 @@ static void decoder_run_song(const struct song *song, const char *uri)
const char *s = uri_get_suffix(uri); const char *s = uri_get_suffix(uri);
while ((plugin = decoder_plugin_from_suffix(s, next++))) { while ((plugin = decoder_plugin_from_suffix(s, next++))) {
if (plugin->file_decode != NULL) { if (plugin->file_decode != NULL) {
input_stream_close(&input_stream); if (close_instream) {
close_instream = false; input_stream_close(&input_stream);
close_instream = false;
}
ret = decoder_file_decode(plugin, ret = decoder_file_decode(plugin,
&decoder, uri); &decoder, uri);
if (ret) if (ret)
......
...@@ -66,6 +66,7 @@ input_archive_open(struct input_stream *is, const char *pathname) ...@@ -66,6 +66,7 @@ input_archive_open(struct input_stream *is, const char *pathname)
if (!opened) { if (!opened) {
g_warning("open inarchive file %s failed\n\n",filename); g_warning("open inarchive file %s failed\n\n",filename);
arplug->close(file);
} else { } else {
is->ready = true; is->ready = true;
} }
......
...@@ -41,9 +41,6 @@ ...@@ -41,9 +41,6 @@
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_curl" #define G_LOG_DOMAIN "input_curl"
/** rewinding is possible after up to 64 kB */
static const off_t max_rewind_size = 64 * 1024;
/** /**
* Buffers created by input_curl_writefunction(). * Buffers created by input_curl_writefunction().
*/ */
...@@ -78,9 +75,6 @@ struct input_curl { ...@@ -78,9 +75,6 @@ struct input_curl {
/** did libcurl tell us the we're at the end of the response body? */ /** did libcurl tell us the we're at the end of the response body? */
bool eof; bool eof;
/** limited list of old buffers, for rewinding */
GQueue *rewind;
/** error message provided by libcurl */ /** error message provided by libcurl */
char error[CURL_ERROR_SIZE]; char error[CURL_ERROR_SIZE];
...@@ -176,11 +170,6 @@ input_curl_easy_free(struct input_curl *c) ...@@ -176,11 +170,6 @@ input_curl_easy_free(struct input_curl *c)
g_queue_foreach(c->buffers, buffer_free_callback, NULL); g_queue_foreach(c->buffers, buffer_free_callback, NULL);
g_queue_clear(c->buffers); g_queue_clear(c->buffers);
if (c->rewind != NULL) {
g_queue_foreach(c->rewind, buffer_free_callback, NULL);
g_queue_clear(c->rewind);
}
} }
/** /**
...@@ -201,8 +190,6 @@ input_curl_free(struct input_stream *is) ...@@ -201,8 +190,6 @@ input_curl_free(struct input_stream *is)
curl_multi_cleanup(c->multi); curl_multi_cleanup(c->multi);
g_queue_free(c->buffers); g_queue_free(c->buffers);
if (c->rewind != NULL)
g_queue_free(c->rewind);
g_free(c->url); g_free(c->url);
g_free(c); g_free(c);
...@@ -322,7 +309,7 @@ fill_buffer(struct input_stream *is) ...@@ -322,7 +309,7 @@ fill_buffer(struct input_stream *is)
* Mark a part of the buffer object as consumed. * Mark a part of the buffer object as consumed.
*/ */
static struct buffer * static struct buffer *
consume_buffer(struct buffer *buffer, size_t length, GQueue *rewind_buffers) consume_buffer(struct buffer *buffer, size_t length)
{ {
assert(buffer != NULL); assert(buffer != NULL);
assert(buffer->consumed < buffer->size); assert(buffer->consumed < buffer->size);
...@@ -333,19 +320,14 @@ consume_buffer(struct buffer *buffer, size_t length, GQueue *rewind_buffers) ...@@ -333,19 +320,14 @@ consume_buffer(struct buffer *buffer, size_t length, GQueue *rewind_buffers)
assert(buffer->consumed == buffer->size); assert(buffer->consumed == buffer->size);
if (rewind_buffers != NULL) g_free(buffer);
/* append this buffer to the rewind buffer list */
g_queue_push_tail(rewind_buffers, buffer);
else
g_free(buffer);
return NULL; return NULL;
} }
static size_t static size_t
read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers, read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers,
void *dest0, size_t length, void *dest0, size_t length)
GQueue *rewind_buffers)
{ {
struct buffer *buffer = g_queue_pop_head(buffers); struct buffer *buffer = g_queue_pop_head(buffers);
uint8_t *dest = dest0; uint8_t *dest = dest0;
...@@ -364,7 +346,7 @@ read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers, ...@@ -364,7 +346,7 @@ read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers,
if (chunk > 0) { if (chunk > 0) {
memcpy(dest, buffer->data + buffer->consumed, memcpy(dest, buffer->data + buffer->consumed,
chunk); chunk);
buffer = consume_buffer(buffer, chunk, rewind_buffers); buffer = consume_buffer(buffer, chunk);
nbytes += chunk; nbytes += chunk;
dest += chunk; dest += chunk;
...@@ -379,7 +361,7 @@ read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers, ...@@ -379,7 +361,7 @@ read_from_buffer(struct icy_metadata *icy_metadata, GQueue *buffers,
chunk = icy_meta(icy_metadata, buffer->data + buffer->consumed, chunk = icy_meta(icy_metadata, buffer->data + buffer->consumed,
length); length);
if (chunk > 0) { if (chunk > 0) {
buffer = consume_buffer(buffer, chunk, rewind_buffers); buffer = consume_buffer(buffer, chunk);
length -= chunk; length -= chunk;
...@@ -418,31 +400,9 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size) ...@@ -418,31 +400,9 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size)
{ {
struct input_curl *c = is->data; struct input_curl *c = is->data;
bool success; bool success;
GQueue *rewind_buffers;
size_t nbytes = 0; size_t nbytes = 0;
char *dest = ptr; char *dest = ptr;
#ifndef NDEBUG
if (c->rewind != NULL &&
(!g_queue_is_empty(c->rewind) || is->offset == 0)) {
off_t offset = 0;
struct buffer *buffer;
for (GList *list = g_queue_peek_head_link(c->rewind);
list != NULL; list = g_list_next(list)) {
buffer = list->data;
offset += buffer->consumed;
assert(offset <= is->offset);
}
buffer = g_queue_peek_head(c->buffers);
if (buffer != NULL)
offset += buffer->consumed;
assert(offset == is->offset);
}
#endif
do { do {
/* fill the buffer */ /* fill the buffer */
...@@ -452,19 +412,9 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size) ...@@ -452,19 +412,9 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size)
/* send buffer contents */ /* send buffer contents */
if (c->rewind != NULL &&
(!g_queue_is_empty(c->rewind) || is->offset == 0))
/* at the beginning or already writing the rewind
buffer list */
rewind_buffers = c->rewind;
else
/* we don't need the rewind buffers anymore */
rewind_buffers = NULL;
while (size > 0 && !g_queue_is_empty(c->buffers)) { while (size > 0 && !g_queue_is_empty(c->buffers)) {
size_t copy = read_from_buffer(&c->icy_metadata, c->buffers, size_t copy = read_from_buffer(&c->icy_metadata, c->buffers,
dest + nbytes, size, dest + nbytes, size);
rewind_buffers);
nbytes += copy; nbytes += copy;
size -= copy; size -= copy;
...@@ -476,33 +426,6 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size) ...@@ -476,33 +426,6 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size)
is->offset += (off_t)nbytes; is->offset += (off_t)nbytes;
#ifndef NDEBUG
if (rewind_buffers != NULL) {
off_t offset = 0;
struct buffer *buffer;
for (GList *list = g_queue_peek_head_link(c->rewind);
list != NULL; list = g_list_next(list)) {
buffer = list->data;
offset += buffer->consumed;
assert(offset <= is->offset);
}
buffer = g_queue_peek_head(c->buffers);
if (buffer != NULL)
offset += buffer->consumed;
assert(offset == is->offset);
}
#endif
if (rewind_buffers != NULL && is->offset > max_rewind_size) {
/* drop the rewind buffer, it has grown too large */
g_queue_foreach(c->rewind, buffer_free_callback, NULL);
g_queue_clear(c->rewind);
}
return nbytes; return nbytes;
} }
...@@ -635,15 +558,6 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream) ...@@ -635,15 +558,6 @@ input_curl_headerfunction(void *ptr, size_t size, size_t nmemb, void *stream)
/* a stream with icy-metadata is not /* a stream with icy-metadata is not
seekable */ seekable */
is->seekable = false; is->seekable = false;
if (c->rewind != NULL) {
/* rewinding with icy-metadata is too
hairy for me .. */
assert(g_queue_is_empty(c->rewind));
g_queue_free(c->rewind);
c->rewind = NULL;
}
} }
} }
...@@ -732,6 +646,18 @@ input_curl_easy_init(struct input_stream *is) ...@@ -732,6 +646,18 @@ input_curl_easy_init(struct input_stream *is)
return true; return true;
} }
void
input_curl_reinit(struct input_stream *is)
{
struct input_curl *c = is->data;
assert(is->plugin == &input_plugin_curl);
assert(c->easy != NULL);
curl_easy_setopt(c->easy, CURLOPT_WRITEHEADER, is);
curl_easy_setopt(c->easy, CURLOPT_WRITEDATA, is);
}
static bool static bool
input_curl_send_request(struct input_curl *c) input_curl_send_request(struct input_curl *c)
{ {
...@@ -752,72 +678,6 @@ input_curl_send_request(struct input_curl *c) ...@@ -752,72 +678,6 @@ input_curl_send_request(struct input_curl *c)
} }
static bool static bool
input_curl_can_rewind(struct input_stream *is)
{
struct input_curl *c = is->data;
struct buffer *buffer;
if (c->rewind == NULL)
return false;
if (!g_queue_is_empty(c->rewind))
/* the rewind buffer hasn't been wiped yet */
return true;
if (g_queue_is_empty(c->buffers))
/* there are no buffers at all - cheap rewind not
possible */
return false;
/* rewind is possible if this is the very first buffer of the
resource */
buffer = (struct buffer*)g_queue_peek_head(c->buffers);
return (off_t)buffer->consumed == is->offset;
}
static void
input_curl_rewind(struct input_stream *is)
{
struct input_curl *c = is->data;
#ifndef NDEBUG
off_t offset = 0;
#endif
assert(c->rewind != NULL);
/* rewind the current buffer */
if (!g_queue_is_empty(c->buffers)) {
struct buffer *buffer =
(struct buffer*)g_queue_peek_head(c->buffers);
#ifndef NDEBUG
offset += buffer->consumed;
#endif
buffer->consumed = 0;
}
/* reset and move all rewind buffers back to the regular buffer list */
while (!g_queue_is_empty(c->rewind)) {
struct buffer *buffer =
(struct buffer*)g_queue_pop_tail(c->rewind);
#ifndef NDEBUG
offset += buffer->consumed;
#endif
buffer->consumed = 0;
g_queue_push_head(c->buffers, buffer);
}
assert(offset == is->offset);
is->offset = 0;
/* rewind the icy_metadata object */
icy_reset(&c->icy_metadata);
}
static bool
input_curl_seek(struct input_stream *is, off_t offset, int whence) input_curl_seek(struct input_stream *is, off_t offset, int whence)
{ {
struct input_curl *c = is->data; struct input_curl *c = is->data;
...@@ -825,17 +685,9 @@ input_curl_seek(struct input_stream *is, off_t offset, int whence) ...@@ -825,17 +685,9 @@ input_curl_seek(struct input_stream *is, off_t offset, int whence)
assert(is->ready); assert(is->ready);
if (whence == SEEK_SET && offset == 0) { if (whence == SEEK_SET && offset == is->offset)
if (is->offset == 0) /* no-op */
/* no-op */ return true;
return true;
if (input_curl_can_rewind(is)) {
/* we have enough rewind buffers left */
input_curl_rewind(is);
return true;
}
}
if (!is->seekable) if (!is->seekable)
return false; return false;
...@@ -868,26 +720,16 @@ input_curl_seek(struct input_stream *is, off_t offset, int whence) ...@@ -868,26 +720,16 @@ input_curl_seek(struct input_stream *is, off_t offset, int whence)
/* check if we can fast-forward the buffer */ /* check if we can fast-forward the buffer */
while (offset > is->offset && !g_queue_is_empty(c->buffers)) { while (offset > is->offset && !g_queue_is_empty(c->buffers)) {
GQueue *rewind_buffers;
struct buffer *buffer; struct buffer *buffer;
size_t length; size_t length;
if (c->rewind != NULL &&
(!g_queue_is_empty(c->rewind) || is->offset == 0))
/* at the beginning or already writing the rewind
buffer list */
rewind_buffers = c->rewind;
else
/* we don't need the rewind buffers anymore */
rewind_buffers = NULL;
buffer = (struct buffer *)g_queue_pop_head(c->buffers); buffer = (struct buffer *)g_queue_pop_head(c->buffers);
length = buffer->size - buffer->consumed; length = buffer->size - buffer->consumed;
if (offset - is->offset < (off_t)length) if (offset - is->offset < (off_t)length)
length = offset - is->offset; length = offset - is->offset;
buffer = consume_buffer(buffer, length, rewind_buffers); buffer = consume_buffer(buffer, length);
if (buffer != NULL) if (buffer != NULL)
g_queue_push_head(c->buffers, buffer); g_queue_push_head(c->buffers, buffer);
...@@ -940,7 +782,6 @@ input_curl_open(struct input_stream *is, const char *url) ...@@ -940,7 +782,6 @@ input_curl_open(struct input_stream *is, const char *url)
c = g_new0(struct input_curl, 1); c = g_new0(struct input_curl, 1);
c->url = g_strdup(url); c->url = g_strdup(url);
c->buffers = g_queue_new(); c->buffers = g_queue_new();
c->rewind = g_queue_new();
is->plugin = &input_plugin_curl; is->plugin = &input_plugin_curl;
is->data = c; is->data = c;
......
...@@ -20,6 +20,16 @@ ...@@ -20,6 +20,16 @@
#ifndef MPD_INPUT_CURL_H #ifndef MPD_INPUT_CURL_H
#define MPD_INPUT_CURL_H #define MPD_INPUT_CURL_H
struct input_stream;
extern const struct input_plugin input_plugin_curl; extern const struct input_plugin input_plugin_curl;
/**
* This is a workaround for an input_stream API deficiency; after
* exchanging the input_stream pointer in input_rewind_open(), this
* function is called to reinitialize CURL's data pointers.
*/
void
input_curl_reinit(struct input_stream *is);
#endif #endif
...@@ -36,25 +36,14 @@ input_file_open(struct input_stream *is, const char *filename) ...@@ -36,25 +36,14 @@ input_file_open(struct input_stream *is, const char *filename)
int fd, ret; int fd, ret;
struct stat st; struct stat st;
char* pathname = g_strdup(filename);
if (filename[0] != '/') if (filename[0] != '/')
{
g_free(pathname);
return false; return false;
}
if (stat(filename, &st) < 0) { fd = open(filename, O_RDONLY);
char* slash = strrchr(pathname, '/');
*slash = '\0';
}
fd = open(pathname, O_RDONLY);
if (fd < 0) { if (fd < 0) {
is->error = errno; is->error = errno;
g_debug("Failed to open \"%s\": %s", g_debug("Failed to open \"%s\": %s",
pathname, g_strerror(errno)); filename, g_strerror(errno));
g_free(pathname);
return false; return false;
} }
...@@ -64,15 +53,13 @@ input_file_open(struct input_stream *is, const char *filename) ...@@ -64,15 +53,13 @@ input_file_open(struct input_stream *is, const char *filename)
if (ret < 0) { if (ret < 0) {
is->error = errno; is->error = errno;
close(fd); close(fd);
g_free(pathname);
return false; return false;
} }
if (!S_ISREG(st.st_mode)) { if (!S_ISREG(st.st_mode)) {
g_debug("Not a regular file: %s", pathname); g_debug("Not a regular file: %s", filename);
is->error = EINVAL; is->error = EINVAL;
close(fd); close(fd);
g_free(pathname);
return false; return false;
} }
...@@ -86,8 +73,6 @@ input_file_open(struct input_stream *is, const char *filename) ...@@ -86,8 +73,6 @@ input_file_open(struct input_stream *is, const char *filename)
is->data = GINT_TO_POINTER(fd); is->data = GINT_TO_POINTER(fd);
is->ready = true; is->ready = true;
g_free(pathname);
return true; return true;
} }
......
...@@ -112,7 +112,7 @@ lastfm_input_open(struct input_stream *is, const char *url) ...@@ -112,7 +112,7 @@ lastfm_input_open(struct input_stream *is, const char *url)
#if GLIB_CHECK_VERSION(2,16,0) #if GLIB_CHECK_VERSION(2,16,0)
q = g_uri_escape_string(lastfm_user, NULL, false); q = g_uri_escape_string(lastfm_user, NULL, false);
#else #else
q = g_strdup(lastfm_username); q = g_strdup(lastfm_user);
#endif #endif
#if GLIB_CHECK_VERSION(2,16,0) #if GLIB_CHECK_VERSION(2,16,0)
......
/*
* Copyright (C) 2003-2009 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.
*/
#include "config.h"
#include "input/rewind_input_plugin.h"
#include "input/curl_input_plugin.h"
#include "input_plugin.h"
#include "tag.h"
#include <glib.h>
#include <assert.h>
#include <stdio.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_rewind"
struct input_rewind {
struct input_stream input;
/**
* The read position within the buffer. Undefined as long as
* reading_from_buffer() returns false.
*/
size_t head;
/**
* The write/append position within the buffer.
*/
size_t tail;
/**
* The size of this buffer is the maximum number of bytes
* which can be rewinded cheaply without passing the "seek"
* call to CURL.
*
* The origin of this buffer is always the beginning of the
* stream (offset 0).
*/
char buffer[64 * 1024];
};
/**
* Are we currently reading from the buffer, and does the buffer
* contain more data for the next read operation?
*/
static bool
reading_from_buffer(const struct input_stream *is)
{
const struct input_rewind *r = is->data;
return r->tail > 0 && is->offset < r->input.offset;
}
/**
* Copy public attributes from the underlying input stream to the
* "rewind" input stream. This function is called when a method of
* the underlying stream has returned, which may have modified these
* attributes.
*/
static void
copy_attributes(struct input_stream *dest)
{
const struct input_rewind *r = dest->data;
const struct input_stream *src = &r->input;
dest->ready = src->ready;
dest->seekable = src->seekable;
dest->error = src->error;
dest->size = src->size;
dest->offset = src->offset;
if (dest->mime == NULL && src->mime != NULL)
/* this is set only once, and the duplicated pointer
is freed by input_stream_close() */
dest->mime = g_strdup(src->mime);
}
static void
input_rewind_close(struct input_stream *is)
{
struct input_rewind *r = is->data;
input_stream_close(&r->input);
g_free(r);
}
static struct tag *
input_rewind_tag(struct input_stream *is)
{
struct input_rewind *r = is->data;
return input_stream_tag(&r->input);
}
static int
input_rewind_buffer(struct input_stream *is)
{
struct input_rewind *r = is->data;
int ret = input_stream_buffer(&r->input);
if (ret < 0 || !reading_from_buffer(is))
copy_attributes(is);
return ret;
}
static size_t
input_rewind_read(struct input_stream *is, void *ptr, size_t size)
{
struct input_rewind *r = is->data;
if (reading_from_buffer(is)) {
/* buffered read */
assert(r->head == (size_t)is->offset);
assert(r->tail == (size_t)r->input.offset);
if (size > r->tail - r->head)
size = r->tail - r->head;
memcpy(ptr, r->buffer + r->head, size);
r->head += size;
is->offset += size;
return size;
} else {
/* pass method call to underlying stream */
size_t nbytes = input_stream_read(&r->input, ptr, size);
if (r->input.offset > (off_t)sizeof(r->buffer))
/* disable buffering */
r->tail = 0;
else if (r->tail == (size_t)is->offset) {
/* append to buffer */
memcpy(r->buffer + r->tail, ptr, nbytes);
r->tail += nbytes;
assert(r->tail == (size_t)r->input.offset);
}
copy_attributes(is);
return nbytes;
}
}
static bool
input_rewind_eof(G_GNUC_UNUSED struct input_stream *is)
{
struct input_rewind *r = is->data;
return !reading_from_buffer(is) && input_stream_eof(&r->input);
}
static bool
input_rewind_seek(struct input_stream *is, off_t offset, int whence)
{
struct input_rewind *r = is->data;
assert(is->ready);
if (whence == SEEK_SET && r->tail > 0 && offset <= (off_t)r->tail) {
/* buffered seek */
assert(!reading_from_buffer(is) ||
r->head == (size_t)is->offset);
assert(r->tail == (size_t)r->input.offset);
r->head = (size_t)offset;
is->offset = offset;
return true;
} else {
bool success = input_stream_seek(&r->input, offset, whence);
copy_attributes(is);
/* disable the buffer, because r->input has left the
buffered range now */
r->tail = 0;
return success;
}
}
static const struct input_plugin rewind_input_plugin = {
.close = input_rewind_close,
.tag = input_rewind_tag,
.buffer = input_rewind_buffer,
.read = input_rewind_read,
.eof = input_rewind_eof,
.seek = input_rewind_seek,
};
void
input_rewind_open(struct input_stream *is)
{
struct input_rewind *c;
assert(is != NULL);
assert(is->offset == 0);
if (is->plugin != &input_plugin_curl)
/* due to limitations in the input_plugin API, we only
(explicitly) support the CURL input plugin */
return;
c = g_new(struct input_rewind, 1);
c->tail = 0;
/* move the CURL input stream to c->input */
c->input = *is;
input_curl_reinit(&c->input);
/* convert the existing input_stream pointer to a "rewind"
input stream */
is->plugin = &rewind_input_plugin;
is->data = c;
}
/*
* Copyright (C) 2003-2009 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.
*/
/** \file
*
* A wrapper for an input_stream object which allows cheap buffered
* rewinding. This is useful while detecting the stream codec (let
* each decoder plugin peek a portion from the stream).
*/
#ifndef MPD_INPUT_REWIND_H
#define MPD_INPUT_REWIND_H
#include "config.h"
struct input_stream;
#ifdef HAVE_CURL
void
input_rewind_open(struct input_stream *is);
#else
static inline void
input_rewind_open(struct input_stream *is)
{
(void)is;
}
#endif
#endif
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "conf.h" #include "conf.h"
#include "input/file_input_plugin.h" #include "input/file_input_plugin.h"
#include "input/rewind_input_plugin.h"
#ifdef ENABLE_ARCHIVE #ifdef ENABLE_ARCHIVE
#include "input/archive_input_plugin.h" #include "input/archive_input_plugin.h"
...@@ -131,6 +132,8 @@ input_stream_open(struct input_stream *is, const char *url) ...@@ -131,6 +132,8 @@ input_stream_open(struct input_stream *is, const char *url)
assert(is->plugin->eof != NULL); assert(is->plugin->eof != NULL);
assert(!is->seekable || is->plugin->seek != NULL); assert(!is->seekable || is->plugin->seek != NULL);
input_rewind_open(is);
return true; return true;
} }
} }
......
...@@ -221,14 +221,19 @@ map_spl_path(void) ...@@ -221,14 +221,19 @@ map_spl_path(void)
char * char *
map_spl_utf8_to_fs(const char *name) map_spl_utf8_to_fs(const char *name)
{ {
char *filename = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL); char *filename_utf8, *filename_fs, *path;
char *path;
if (playlist_dir == NULL) if (playlist_dir == NULL)
return NULL; return NULL;
path = g_build_filename(playlist_dir, filename, NULL); filename_utf8 = g_strconcat(name, PLAYLIST_FILE_SUFFIX, NULL);
g_free(filename); filename_fs = utf8_to_fs_charset(filename_utf8);
g_free(filename_utf8);
if (filename_fs == NULL)
return NULL;
path = g_build_filename(playlist_dir, filename_fs, NULL);
g_free(filename_fs);
return path; return path;
} }
...@@ -99,6 +99,8 @@ map_spl_path(void); ...@@ -99,6 +99,8 @@ map_spl_path(void);
* Maps a playlist name (without the ".m3u" suffix) to a file system * Maps a playlist name (without the ".m3u" suffix) to a file system
* path. The return value is allocated on the heap and must be freed * path. The return value is allocated on the heap and must be freed
* with g_free(). * with g_free().
*
* @return the path in file system encoding, or NULL if mapping failed
*/ */
char * char *
map_spl_utf8_to_fs(const char *name); map_spl_utf8_to_fs(const char *name);
......
...@@ -62,6 +62,10 @@ mixer_free(struct mixer *mixer) ...@@ -62,6 +62,10 @@ mixer_free(struct mixer *mixer)
assert(mixer->plugin != NULL); assert(mixer->plugin != NULL);
assert(mixer->mutex != NULL); assert(mixer->mutex != NULL);
/* mixers with the "global" flag set might still be open at
this point (see mixer_auto_close()) */
mixer_close(mixer);
g_mutex_free(mixer->mutex); g_mutex_free(mixer->mutex);
mixer->plugin->finish(mixer); mixer->plugin->finish(mixer);
......
...@@ -248,6 +248,15 @@ static gpointer audio_output_task(gpointer arg) ...@@ -248,6 +248,15 @@ static gpointer audio_output_task(gpointer arg)
break; break;
case AO_COMMAND_PAUSE: case AO_COMMAND_PAUSE:
if (!ao->open) {
/* the output has failed after
audio_output_all_pause() has
submitted the PAUSE command; bail
out */
ao_command_finished(ao);
break;
}
ao_pause(ao); ao_pause(ao);
/* don't "break" here: this might cause /* don't "break" here: this might cause
ao_play() to be called when command==CLOSE ao_play() to be called when command==CLOSE
......
...@@ -71,12 +71,15 @@ spl_save_queue(const char *name_utf8, const struct queue *queue) ...@@ -71,12 +71,15 @@ spl_save_queue(const char *name_utf8, const struct queue *queue)
char *path_fs; char *path_fs;
FILE *file; FILE *file;
if (map_spl_path() == NULL)
return PLAYLIST_RESULT_DISABLED;
if (!spl_valid_name(name_utf8)) if (!spl_valid_name(name_utf8))
return PLAYLIST_RESULT_BAD_NAME; return PLAYLIST_RESULT_BAD_NAME;
path_fs = map_spl_utf8_to_fs(name_utf8); path_fs = map_spl_utf8_to_fs(name_utf8);
if (path_fs == NULL) if (path_fs == NULL)
return PLAYLIST_RESULT_DISABLED; return PLAYLIST_RESULT_BAD_NAME;
if (g_file_test(path_fs, G_FILE_TEST_EXISTS)) { if (g_file_test(path_fs, G_FILE_TEST_EXISTS)) {
g_free(path_fs); g_free(path_fs);
......
...@@ -45,14 +45,14 @@ queue_next_order(const struct queue *queue, unsigned order) ...@@ -45,14 +45,14 @@ queue_next_order(const struct queue *queue, unsigned order)
if (queue->single) if (queue->single)
{ {
if (queue->repeat) if (queue->repeat && !queue->consume)
return order; return order;
else else
return -1; return -1;
} }
if (order + 1 < queue->length) if (order + 1 < queue->length)
return order + 1; return order + 1;
else if (queue->repeat) else if (queue->repeat && (order > 0 || !queue->consume))
/* restart at first song */ /* restart at first song */
return 0; return 0;
else else
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
#include "song.h" #include "song.h"
#include "tag_save.h" #include "tag_save.h"
#include "directory.h" #include "directory.h"
#include "path.h"
#include "tag.h" #include "tag.h"
#include <glib.h> #include <glib.h>
...@@ -113,12 +112,15 @@ matchesAnMpdTagItemKey(char *buffer, enum tag_type *itemType) ...@@ -113,12 +112,15 @@ matchesAnMpdTagItemKey(char *buffer, enum tag_type *itemType)
void readSongInfoIntoList(FILE *fp, struct songvec *sv, void readSongInfoIntoList(FILE *fp, struct songvec *sv,
struct directory *parent) struct directory *parent)
{ {
char buffer[MPD_PATH_MAX + 1024]; enum {
buffer_size = 32768,
};
char *buffer = g_malloc(buffer_size);
struct song *song = NULL; struct song *song = NULL;
enum tag_type itemType; enum tag_type itemType;
const char *value; const char *value;
while (fgets(buffer, sizeof(buffer), fp) && while (fgets(buffer, buffer_size, fp) &&
!g_str_has_prefix(buffer, SONG_END)) { !g_str_has_prefix(buffer, SONG_END)) {
g_strchomp(buffer); g_strchomp(buffer);
...@@ -156,6 +158,8 @@ void readSongInfoIntoList(FILE *fp, struct songvec *sv, ...@@ -156,6 +158,8 @@ void readSongInfoIntoList(FILE *fp, struct songvec *sv,
g_error("unknown line in db: %s", buffer); g_error("unknown line in db: %s", buffer);
} }
g_free(buffer);
if (song) if (song)
insertSongIntoList(sv, song); insertSongIntoList(sv, song);
} }
...@@ -27,6 +27,10 @@ ...@@ -27,6 +27,10 @@
#undef G_LOG_DOMAIN #undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "sticker" #define G_LOG_DOMAIN "sticker"
#if SQLITE_VERSION_NUMBER < 3003009
#define sqlite3_prepare_v2 sqlite3_prepare
#endif
struct sticker { struct sticker {
GHashTable *table; GHashTable *table;
}; };
......
...@@ -152,9 +152,12 @@ spl_save(GPtrArray *list, const char *utf8path) ...@@ -152,9 +152,12 @@ spl_save(GPtrArray *list, const char *utf8path)
assert(utf8path != NULL); assert(utf8path != NULL);
if (map_spl_path() == NULL)
return PLAYLIST_RESULT_DISABLED;
path_fs = map_spl_utf8_to_fs(utf8path); path_fs = map_spl_utf8_to_fs(utf8path);
if (path_fs == NULL) if (path_fs == NULL)
return PLAYLIST_RESULT_DISABLED; return PLAYLIST_RESULT_BAD_NAME;
while (!(file = fopen(path_fs, "w")) && errno == EINTR); while (!(file = fopen(path_fs, "w")) && errno == EINTR);
g_free(path_fs); g_free(path_fs);
...@@ -178,7 +181,7 @@ spl_load(const char *utf8path) ...@@ -178,7 +181,7 @@ spl_load(const char *utf8path)
char buffer[MPD_PATH_MAX]; char buffer[MPD_PATH_MAX];
char *path_fs; char *path_fs;
if (!spl_valid_name(utf8path)) if (!spl_valid_name(utf8path) || map_spl_path() == NULL)
return NULL; return NULL;
path_fs = map_spl_utf8_to_fs(utf8path); path_fs = map_spl_utf8_to_fs(utf8path);
...@@ -299,12 +302,15 @@ spl_clear(const char *utf8path) ...@@ -299,12 +302,15 @@ spl_clear(const char *utf8path)
char *path_fs; char *path_fs;
FILE *file; FILE *file;
if (map_spl_path() == NULL)
return PLAYLIST_RESULT_DISABLED;
if (!spl_valid_name(utf8path)) if (!spl_valid_name(utf8path))
return PLAYLIST_RESULT_BAD_NAME; return PLAYLIST_RESULT_BAD_NAME;
path_fs = map_spl_utf8_to_fs(utf8path); path_fs = map_spl_utf8_to_fs(utf8path);
if (path_fs == NULL) if (path_fs == NULL)
return PLAYLIST_RESULT_DISABLED; return PLAYLIST_RESULT_BAD_NAME;
while (!(file = fopen(path_fs, "w")) && errno == EINTR); while (!(file = fopen(path_fs, "w")) && errno == EINTR);
g_free(path_fs); g_free(path_fs);
...@@ -323,9 +329,15 @@ spl_delete(const char *name_utf8) ...@@ -323,9 +329,15 @@ spl_delete(const char *name_utf8)
char *path_fs; char *path_fs;
int ret; int ret;
if (map_spl_path() == NULL)
return PLAYLIST_RESULT_DISABLED;
if (!spl_valid_name(name_utf8))
return PLAYLIST_RESULT_BAD_NAME;
path_fs = map_spl_utf8_to_fs(name_utf8); path_fs = map_spl_utf8_to_fs(name_utf8);
if (path_fs == NULL) if (path_fs == NULL)
return PLAYLIST_RESULT_DISABLED; return PLAYLIST_RESULT_BAD_NAME;
ret = unlink(path_fs); ret = unlink(path_fs);
g_free(path_fs); g_free(path_fs);
...@@ -370,12 +382,15 @@ spl_append_song(const char *utf8path, struct song *song) ...@@ -370,12 +382,15 @@ spl_append_song(const char *utf8path, struct song *song)
struct stat st; struct stat st;
char *path_fs; char *path_fs;
if (map_spl_path() == NULL)
return PLAYLIST_RESULT_DISABLED;
if (!spl_valid_name(utf8path)) if (!spl_valid_name(utf8path))
return PLAYLIST_RESULT_BAD_NAME; return PLAYLIST_RESULT_BAD_NAME;
path_fs = map_spl_utf8_to_fs(utf8path); path_fs = map_spl_utf8_to_fs(utf8path);
if (path_fs == NULL) if (path_fs == NULL)
return PLAYLIST_RESULT_DISABLED; return PLAYLIST_RESULT_BAD_NAME;
while (!(file = fopen(path_fs, "a")) && errno == EINTR); while (!(file = fopen(path_fs, "a")) && errno == EINTR);
g_free(path_fs); g_free(path_fs);
...@@ -445,6 +460,9 @@ spl_rename(const char *utf8from, const char *utf8to) ...@@ -445,6 +460,9 @@ spl_rename(const char *utf8from, const char *utf8to)
char *from_path_fs, *to_path_fs; char *from_path_fs, *to_path_fs;
static enum playlist_result ret; static enum playlist_result ret;
if (map_spl_path() == NULL)
return PLAYLIST_RESULT_DISABLED;
if (!spl_valid_name(utf8from) || !spl_valid_name(utf8to)) if (!spl_valid_name(utf8from) || !spl_valid_name(utf8to))
return PLAYLIST_RESULT_BAD_NAME; return PLAYLIST_RESULT_BAD_NAME;
...@@ -454,7 +472,7 @@ spl_rename(const char *utf8from, const char *utf8to) ...@@ -454,7 +472,7 @@ spl_rename(const char *utf8from, const char *utf8to)
if (from_path_fs != NULL && to_path_fs != NULL) if (from_path_fs != NULL && to_path_fs != NULL)
ret = spl_rename_internal(from_path_fs, to_path_fs); ret = spl_rename_internal(from_path_fs, to_path_fs);
else else
ret = PLAYLIST_RESULT_DISABLED; ret = PLAYLIST_RESULT_BAD_NAME;
g_free(from_path_fs); g_free(from_path_fs);
g_free(to_path_fs); g_free(to_path_fs);
......
...@@ -90,7 +90,7 @@ static id3_utf8_t * processID3FieldString (int is_id3v1, const id3_ucs4_t *ucs4, ...@@ -90,7 +90,7 @@ static id3_utf8_t * processID3FieldString (int is_id3v1, const id3_ucs4_t *ucs4,
utf8 = (id3_utf8_t *) utf8 = (id3_utf8_t *)
g_convert_with_fallback((const char*)isostr, -1, g_convert_with_fallback((const char*)isostr, -1,
encoding, "utf-8", "utf-8", encoding,
NULL, NULL, NULL, NULL); NULL, NULL, NULL, NULL);
if (utf8 == NULL) { if (utf8 == NULL) {
g_debug("Unable to convert %s string to UTF-8: '%s'", g_debug("Unable to convert %s string to UTF-8: '%s'",
...@@ -481,7 +481,7 @@ tag_id3_riff_aiff_load(FILE *file) ...@@ -481,7 +481,7 @@ tag_id3_riff_aiff_load(FILE *file)
if (size == 0) if (size == 0)
return NULL; return NULL;
if (size > 256 * 1024) if (size > 4 * 1024 * 1024)
/* too large, don't allocate so much memory */ /* too large, don't allocate so much memory */
return NULL; return NULL;
......
...@@ -672,7 +672,11 @@ updateDirectory(struct directory *directory, const struct stat *st) ...@@ -672,7 +672,11 @@ updateDirectory(struct directory *directory, const struct stat *st)
continue; continue;
utf8 = fs_charset_to_utf8(ent->d_name); utf8 = fs_charset_to_utf8(ent->d_name);
if (utf8 == NULL || skip_symlink(directory, utf8)) { if (utf8 == NULL)
continue;
if (skip_symlink(directory, utf8)) {
delete_name_in(directory, utf8);
g_free(utf8); g_free(utf8);
continue; continue;
} }
......
...@@ -17,11 +17,16 @@ ...@@ -17,11 +17,16 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/ */
#include "config.h"
#include "input_stream.h" #include "input_stream.h"
#include "tag_pool.h" #include "tag_pool.h"
#include "tag_save.h" #include "tag_save.h"
#include "conf.h" #include "conf.h"
#ifdef ENABLE_ARCHIVE
#include "archive_list.h"
#endif
#include <glib.h> #include <glib.h>
#include <unistd.h> #include <unistd.h>
...@@ -36,40 +41,17 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level, ...@@ -36,40 +41,17 @@ my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
g_printerr("%s\n", message); g_printerr("%s\n", message);
} }
int main(int argc, char **argv) static int
dump_input_stream(struct input_stream *is)
{ {
struct input_stream is;
bool success;
char buffer[4096]; char buffer[4096];
size_t num_read; size_t num_read;
ssize_t num_written; ssize_t num_written;
if (argc != 2) { /* wait until the stream becomes ready */
g_printerr("Usage: run_input URI\n");
return 1;
}
/* initialize GLib */
g_thread_init(NULL);
g_log_set_default_handler(my_log_func, NULL);
/* initialize MPD */
tag_pool_init(); while (!is->ready) {
config_global_init(); int ret = input_stream_buffer(is);
input_stream_global_init();
/* open the stream and wait until it becomes ready */
success = input_stream_open(&is, argv[1]);
if (!success) {
g_printerr("input_stream_open() failed\n");
return 2;
}
while (!is.ready) {
int ret = input_stream_buffer(&is);
if (ret < 0) if (ret < 0)
/* error */ /* error */
return 2; return 2;
...@@ -81,20 +63,20 @@ int main(int argc, char **argv) ...@@ -81,20 +63,20 @@ int main(int argc, char **argv)
/* print meta data */ /* print meta data */
if (is.mime != NULL) if (is->mime != NULL)
g_printerr("MIME type: %s\n", is.mime); g_printerr("MIME type: %s\n", is->mime);
/* read data and tags from the stream */ /* read data and tags from the stream */
while (!input_stream_eof(&is)) { while (!input_stream_eof(is)) {
struct tag *tag = input_stream_tag(&is); struct tag *tag = input_stream_tag(is);
if (tag != NULL) { if (tag != NULL) {
g_printerr("Received a tag:\n"); g_printerr("Received a tag:\n");
tag_save(stderr, tag); tag_save(stderr, tag);
tag_free(tag); tag_free(tag);
} }
num_read = input_stream_read(&is, buffer, sizeof(buffer)); num_read = input_stream_read(is, buffer, sizeof(buffer));
if (num_read == 0) if (num_read == 0)
break; break;
...@@ -103,12 +85,55 @@ int main(int argc, char **argv) ...@@ -103,12 +85,55 @@ int main(int argc, char **argv)
break; break;
} }
return 0;
}
int main(int argc, char **argv)
{
struct input_stream is;
int ret;
if (argc != 2) {
g_printerr("Usage: run_input URI\n");
return 1;
}
/* initialize GLib */
g_thread_init(NULL);
g_log_set_default_handler(my_log_func, NULL);
/* initialize MPD */
tag_pool_init();
config_global_init();
#ifdef ENABLE_ARCHIVE
archive_plugin_init_all();
#endif
input_stream_global_init();
/* open the stream and dump it */
if (input_stream_open(&is, argv[1])) {
ret = dump_input_stream(&is);
input_stream_close(&is);
} else {
g_printerr("input_stream_open() failed\n");
ret = 2;
}
/* deinitialize everything */ /* deinitialize everything */
input_stream_close(&is);
input_stream_global_finish(); input_stream_global_finish();
#ifdef ENABLE_ARCHIVE
archive_plugin_deinit_all();
#endif
config_global_finish(); config_global_finish();
tag_pool_deinit(); tag_pool_deinit();
return 0; return ret;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment