Compare commits

...

81 Commits

Author SHA1 Message Date
Avuton Olrich
db1ce4eeeb mpd version 0.16.8 2012-04-04 18:37:47 -07:00
Max Kellermann
5acee73fc8 encoder/vorbis: generate end-of-stream packet when playback ends
Add the encoder_plugin method end().  This is important for the
recorder plugin.
2012-04-05 00:21:53 +02:00
Max Kellermann
466c337bcb encoder_plugin: add state assertions 2012-04-05 00:05:21 +02:00
Max Kellermann
98a468a101 encoder/vorbis: generate end-of-stream packet before tag
Don't reset the ogg_stream_state object, because this discards the
end-of-stream packet that was just added.
2012-04-04 23:59:06 +02:00
Max Kellermann
47c58c01d1 test/test_vorbis_encoder: program to debug the vorbis encoder 2012-04-04 23:58:00 +02:00
Max Kellermann
a9edf85a69 output/jack: check for connection failure before starting playback 2012-04-04 21:40:56 +02:00
Max Kellermann
e7a1862517 output/jack: workaround for libjack1 crash bug 2012-04-04 21:38:29 +02:00
Max Kellermann
d8e423df1a directory: use strrchr() instead of g_basename()
g_basename() is deprecated in GLib 2.32.
2012-04-04 19:08:05 +02:00
Max Kellermann
09aa0dc676 uri: remove g_basename() call from uri_get_suffix()
g_basename() is deprecated in GLib 2.32.  Instead, verify that the
suffix does not have a backslash, to catch Windows path names.
2012-04-04 12:22:16 +02:00
Anton Khirnov
83174de420 update: properly skip symlinks in path that is to be updated. 2012-04-04 08:56:45 +02:00
Max Kellermann
8ff0197a43 output/osx: use the fifo_buffer library instead of rolling own
The existing buffer implementation has a major flaw: it is unable to
re-fill the buffer until it has been consumed completely, leading to
many occasions where the render callback needs to generate silence,
just because the play() implementation was unable to append more
data.  The fifo_buffer library handles that well.
2012-03-28 21:51:17 +02:00
Dan McGee
de0f46b947 Use g_message and not g_debug when removing song
When adding or updating a song, we get a log message even if debug is not
enabled. It seems odd that removing a song shouldn't be done at the same log
level; otherwise looking at the log leads you to believe songs are never
removed from the library on update.

Signed-off-by: Dan McGee <dan@archlinux.org>
2012-03-26 17:32:18 +02:00
Max Kellermann
79eb7623ef event_pipe, test: explicitly ignore write() return value
Some compilers are very picky, but we really aren't interested in the
return value.
2012-03-19 23:26:47 +01:00
Jonathan Neuschäfer
b9e64d0472 decoder/audiofile: fix compiler warnings with libaudiofile 0.3.3
This might break older versions, I didn't test.
2012-03-19 23:21:12 +01:00
Max Kellermann
4f500149af text_input_stream: detect end-of-file
Fixes endless loop when the last line of a text file was not
terminated (bug 3470).
2012-03-19 23:17:56 +01:00
Max Kellermann
103832742d decoder/ffmpeg: read the "year" tag
This was disabled when compiled with a new ffmpeg version.  Older
ffmpeg versions used it explicitly, while newer ones may pass it
through from the codec.
2012-02-13 19:05:39 +01:00
Max Kellermann
3e7e0bcb18 test/run_decoder: initialize GThread 2012-02-13 19:00:23 +01:00
Max Kellermann
7d3d8f20ab test/read_tags: call g_thread_init() 2012-02-13 18:37:09 +01:00
Max Kellermann
e1e3ce980a decoder_api: check state before emitting initial seek command
This fixes seeking in the vorbis decoder during MPD startup.
2012-02-13 18:27:43 +01:00
Max Kellermann
7855a32579 pcm_buffer: pcm_buffer_get() never returns NULL
This fixes a bug when libsamplerate returns an empty buffer for a very
small input buffer.  The caller thinks this is an error, bug there is
no GError object.
2012-02-13 18:17:05 +01:00
Max Kellermann
9c92afa5fe output/winmm: remove pointless NULL check
pcm_buffer_get() cannot ever return NULL.
2012-02-13 18:10:36 +01:00
Avuton Olrich
66235fddff Modify version string to post-release version 0.16.8~git 2012-02-04 14:41:59 -08:00
Avuton Olrich
f9c5d026f4 mpd version 0.16.7 2012-02-04 14:41:59 -08:00
Max Kellermann
48eb3ff8d9 test/run_decoder: initialize the tag_pool library 2012-02-04 17:18:37 +01:00
Max Kellermann
5646dcc791 test/read_tags: initialize the tag_pool library 2012-02-04 14:32:17 +01:00
Max Kellermann
5d9876e338 decoder/ffmpeg: use AV_SAMPLE_FMT_* if available
Implements support for libavcodec 0.9, which removes the compatibility
macros SAMPLE_FMT_*
2012-02-03 09:55:25 +01:00
Max Kellermann
083340a937 decoder/ffmpeg: use sentinel for the ffmpeg_tag_maps table
Minor optimisation.
2012-02-03 09:18:05 +01:00
Max Kellermann
378fa5ee6a decoder/ffmpeg: support all MPD tags
Use the tag_item_names table to look up the names of all MPD tags, and
remove the duplicate entries from ffmpeg_tag_maps.
2012-02-03 09:10:48 +01:00
Max Kellermann
4764daf3c2 decoder/ffmpeg: pass tag_type and name to _copy_metadata()
Allow using this function without the ffmpeg_tag_map struct.
2012-02-03 09:09:18 +01:00
Max Kellermann
6357496d17 decoder/ffmpeg: merge code to _copy_dictionary()
Eliminate some duplicate code.
2012-02-03 09:09:18 +01:00
Max Kellermann
001e2a604b decoder/ffmpeg: add macros emulating AVDictionary
Move the #ifdefs out of _copy_metadata().
2012-02-03 09:02:14 +01:00
Max Kellermann
f370911c15 decoder/ffmpeg: _copy_metadata() returns void
No interest in this return value.
2012-02-03 08:59:26 +01:00
Max Kellermann
39d52762d1 decoder/ffmpeg: check libavutil version for AVDictionaryEntry
Require libavutil 51.5.0.
2012-01-12 18:45:18 +01:00
Max Kellermann
8d45d0d104 decoder/ffmpeg: raise version dependency for avformat_find_stream_info()
This function was added when the libavformat version was 53.2.0, but
the actual release 53.2.0 did not have it.
2012-01-12 18:28:19 +01:00
Max Kellermann
abd1949825 decoder/ffmpeg: support libavformat 0.8 2012-01-05 00:17:56 +01:00
Max Kellermann
4e6bc77a70 decoder/ffmpeg: use avcodec_decode_audio4(), support libavcodec 0.8 2012-01-04 22:10:38 +01:00
Max Kellermann
531948358b decoder/ffmpeg: include libavutil/mathematics.h
Needed for av_rescale_q() in ffmpeg 0.8.
2012-01-04 21:54:54 +01:00
Max Kellermann
0d3ec9c324 configure.ac: disable -Wno-deprecated-declarations 2012-01-04 21:48:30 +01:00
Max Kellermann
21caca4aea decoder/ffmpeg: use avcodec_open2() on newer ffmpeg versions
avcodec_open() has been deprecated.
2012-01-04 21:48:30 +01:00
Max Kellermann
fbf3edf07d decoder/ffpmeg: don't use av_metadata_conv() in ffmpeg 0.7
It's a no-op and deprecated.
2012-01-04 21:47:56 +01:00
Max Kellermann
76fcf25898 decoder/ffmpeg: use AVIOContext instead of ByteIOContext 2012-01-04 21:47:47 +01:00
Max Kellermann
56257f072b input/ffmpeg: use the new AVIOContext API
URLContext is deprecated.
2012-01-04 21:47:19 +01:00
Max Kellermann
44401158e8 input/ffmpeg: define AV_VERSION_INT if not present
Support ancient ffmpeg versions.
2012-01-04 21:47:01 +01:00
Max Kellermann
97b4a6b51f output/osx: clear render buffer when there's not enough data
When we don't have enough data, generate some silence, hoping the
input buffer will fill soon.  Reducing the render buffer size is not
legal.
2011-12-24 17:59:36 +01:00
Max Kellermann
f405d27c56 output/osx: remove sleep call from render callback
Blocking inside the render callback is forbidden, and this sleep call
didn't make any sense.
2011-12-24 17:56:10 +01:00
Maarten Sebregts
3a9697adf2 Playlist: fix bug in moving after current song
Moving songs using either 'move' or 'moveid' to position -1 (after the
current song) would fail for a song which is just before the current
song.
This patch corrects the check to see if the current song is in the range
to be moved. Since the range is from `start` up to `end` (exclusive) the
check was incorrect, but is now fixed.
2011-12-21 10:29:07 +01:00
Max Kellermann
96ad5b8444 output/openal: force 16 bit playback, as 8 bit doesn't work
The OpenAL specification says that AL_FORMAT_MONO8 and
AL_FORMAT_STEREO8 expect unsigned 8 bit samples, but MPD uses unsigned
samples.
2011-12-13 21:32:19 +01:00
Max Kellermann
097e5dfbdc timer: fix time unit mixup in timer_delay()
The local variable was already divided by 1000, and the return value
was being divided by 1000 again - doh!  This caused delays in the
httpd output plugin that were too small by three orders of magnitude,
and the buffer was filled too quickly.
2011-12-13 21:02:48 +01:00
Max Kellermann
2ef7ee6ca7 update_walk: print debug message for song_file_load() 2011-12-13 20:26:24 +01:00
Max Kellermann
2685b53b30 configure.ac: suppress warnings in the GLib headers
Replace -I with -isystem in GLIB_CFLAGS.
2011-12-13 20:12:49 +01:00
Max Kellermann
533e4fcdad decoder/mp4ff: work around assertion failure in read() callback
This workaround leads to an infinite loop instead of an assertion
failure, but hey, now it's libmp4ff's fault.
2011-12-13 20:08:31 +01:00
Avuton Olrich
f5d3859238 cmdline: Remove duplicate g_free()s 2011-12-12 09:20:00 +01:00
Avuton Olrich
ef39da5973 configure/utils: Add ipv6 support for mingw build 2011-12-12 09:19:34 +01:00
Avuton Olrich
81e8c4bbff gitignore: Add mpd.service 2011-12-12 09:16:51 +01:00
Avuton Olrich
8ca3642429 Modify version string to post-release version 0.16.7~git 2011-12-01 05:44:53 -08:00
Avuton Olrich
1dc000c06a mpd version 0.16.6 2011-12-01 05:44:53 -08:00
Max Kellermann
e1b032cbad decoder/ffmpeg: work around bogus channel count
Initialize the audio_format before calling avcodec_open(), because
avcodec_open() will fill bogus values.
2011-11-28 11:39:21 +01:00
Max Kellermann
6f365c30eb mapper: check "r" permission on music directory
Yet another common support case.
2011-11-28 09:57:21 +01:00
Max Kellermann
718e180423 mapper: check "x" permission on music directory
This is a common support case, and hopefully, the new error message
will allow the user to understand the error without requiring support.
2011-11-28 09:51:21 +01:00
Max Kellermann
cead5e5bd7 mapper: fix the bogus "not a directory" error message
Use stat() instead of g_file_test() to detect other types of errors,
such as "permission denied".
2011-11-28 09:50:44 +01:00
Max Kellermann
cf15629aea mapper: move code to check_directory() 2011-11-28 09:35:50 +01:00
Max Kellermann
a727d0bb0b log: print reason for failure 2011-11-28 09:31:43 +01:00
Max Kellermann
0a218ee56a encoder/wave: support packed 24 bit samples
Convert to padded 24 bit samples, instead of falling back to 16 bit.
2011-11-28 09:25:42 +01:00
Max Kellermann
74beefcaf6 encoder/null: use fifo_buffer instead of pcm_buffer
This fixes a buffer corruption bug; pcm_buffer is not designed to be a
persistent buffers, and will discard anything between two consecutive
calls.
2011-11-28 09:23:36 +01:00
Max Kellermann
399a3abefc encoder/wave: use fifo_buffer instead of pcm_buffer
This fixes a buffer corruption bug; pcm_buffer is not designed to be a
persistent buffers, and will discard anything between two consecutive
calls.
2011-11-28 09:23:12 +01:00
Max Kellermann
cee5036aca encoder/flac: use fifo_buffer instead of pcm_buffer
This fixes a buffer corruption bug; pcm_buffer is not designed to be a
persistent buffers, and will discard anything between two consecutive
calls.
2011-11-28 09:21:32 +01:00
Max Kellermann
790823abb4 growing_fifo: new utility library for growing fifo_buffer 2011-11-28 09:11:11 +01:00
Max Kellermann
f546849352 fifo_buffer: add function fifo_buffer_realloc()
For growing FIFO buffers.
2011-11-28 07:45:15 +01:00
Max Kellermann
a85af593f1 fifo_buffer: add functions _capacity() and _available() 2011-11-27 21:11:47 +01:00
Max Kellermann
07067f8b95 pcm_buffer: add assertions 2011-11-27 20:17:52 +01:00
Max Kellermann
a1e824ada0 pcm_buffer: move formula to new function align_8k() 2011-11-27 20:17:14 +01:00
Max Kellermann
f5f1bfbef1 pcm_buffer: un-inline pcm_buffer_get()
This method is too complex for inlining.
2011-11-27 20:17:12 +01:00
Max Kellermann
cd108ba3aa directory: rename attribute "stat" to "have_stat"
"stat" is a macro on mingw32, which is a pretty stupid thing, and this
commit works around this build failure.
2011-11-27 20:15:25 +01:00
Max Kellermann
2bb5bfa74e directory: convert "stat" to a bool 2011-11-27 20:11:45 +01:00
Max Kellermann
624e7a447d stats: explicitly cast "time_t" to "long"
Fixes warning on mingw32.
2011-11-27 20:07:14 +01:00
Max Kellermann
ef40e362c9 decoder_api: cancel initial seek when song is not seekable
Fixes assertion failure.
2011-11-27 19:19:43 +01:00
Denis Krjuchkov
6452461c39 path: autodetect filesystem encoding on Win32
WinAPI explicitly declares filesystem encoding.
It can be determined by GetACP().
Use that instead of Glib routine that always "detects" UTF-8 on Win32,
which is incorrect for MPD case.
2011-10-23 16:29:58 +02:00
Max Kellermann
c30c46cd5f configure.ac: define WINVER
Ensure that WINVER is defined early enough, so other system headers
won't fall back to their default value.  Specifically, this solves a
build failure (-Werror) with mingw-w64 ("WINVER redefined").
2011-10-13 09:23:32 +02:00
Max Kellermann
d394017926 decoder_thread: add missing stdio.h include 2011-10-13 09:09:58 +02:00
Max Kellermann
04525c0259 event_pipe: fix WIN32 regression
The event pipe is not a socket, and the patch that introduced
g_io_channel_new_socket() to the event pipe library was wrong.
2011-10-13 09:08:37 +02:00
Avuton Olrich
fa5e06f95d Modify version string to post-release version 0.16.6~git 2011-10-09 04:44:51 -07:00
53 changed files with 1099 additions and 248 deletions

1
.gitignore vendored

@@ -34,6 +34,7 @@ missing
mkinstalldirs
mpd
mpd.exe
mpd.service
stamp-h1
tags
*~

@@ -85,7 +85,6 @@ mpd_headers = \
src/encoder_api.h \
src/exclude.h \
src/fd_util.h \
src/fifo_buffer.h \
src/glib_compat.h \
src/update.h \
src/update_internal.h \
@@ -144,7 +143,6 @@ mpd_headers = \
src/output/pulse_output_plugin.h \
src/output/winmm_output_plugin.h \
src/page.h \
src/pcm_buffer.h \
src/pcm_utils.h \
src/pcm_convert.h \
src/pcm_volume.h \
@@ -267,7 +265,8 @@ src_mpd_SOURCES = \
src/dirvec.c \
src/exclude.c \
src/fd_util.c \
src/fifo_buffer.c \
src/fifo_buffer.c src/fifo_buffer.h \
src/growing_fifo.c src/growing_fifo.h \
src/filter_config.c \
src/filter_plugin.c \
src/filter_registry.c \
@@ -300,6 +299,7 @@ src_mpd_SOURCES = \
src/path.c \
src/mapper.c \
src/page.c \
src/pcm_buffer.c src/pcm_buffer.h \
src/pcm_convert.c \
src/pcm_volume.c \
src/pcm_mix.c \
@@ -928,6 +928,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \
src/audio_check.c \
src/audio_format.c \
src/timer.c \
src/pcm_buffer.c \
$(ARCHIVE_SRC) \
$(INPUT_SRC) \
$(TAG_SRC) \
@@ -950,6 +951,7 @@ test_read_tags_SOURCES = test/read_tags.c \
src/fd_util.c \
src/audio_check.c \
src/timer.c \
src/pcm_buffer.c \
$(ARCHIVE_SRC) \
$(INPUT_SRC) \
$(TAG_SRC) \
@@ -968,6 +970,7 @@ test_run_filter_SOURCES = test/run_filter.c \
src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \
src/pcm_pack.c \
src/pcm_resample.c src/pcm_resample_fallback.c \
src/pcm_buffer.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
@@ -990,6 +993,8 @@ test_run_encoder_SOURCES = test/run_encoder.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
src/pcm_buffer.c \
src/fifo_buffer.c src/growing_fifo.c \
$(ENCODER_SRC)
test_run_encoder_CPPFLAGS = $(AM_CPPFLAGS) \
$(ENCODER_CFLAGS)
@@ -998,6 +1003,26 @@ test_run_encoder_LDADD = $(MPD_LIBS) \
$(GLIB_LIBS)
endif
if ENABLE_VORBIS_ENCODER
noinst_PROGRAMS += test/test_vorbis_encoder
test_test_vorbis_encoder_SOURCES = test/test_vorbis_encoder.c \
test/stdbin.h \
src/conf.c src/tokenizer.c \
src/utils.c \
src/tag.c src/tag_pool.c \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
src/pcm_buffer.c \
src/fifo_buffer.c src/growing_fifo.c \
$(ENCODER_SRC)
test_test_vorbis_encoder_CPPFLAGS = $(AM_CPPFLAGS) \
$(ENCODER_CFLAGS)
test_test_vorbis_encoder_LDADD = $(MPD_LIBS) \
$(ENCODER_LIBS) \
$(GLIB_LIBS)
endif
test_software_volume_SOURCES = test/software_volume.c \
test/stdbin.h \
src/audio_check.c \
@@ -1019,6 +1044,7 @@ test_run_convert_SOURCES = test/run_convert.c \
src/audio_format.c \
src/audio_check.c \
src/audio_parser.c \
src/pcm_buffer.c \
src/pcm_channels.c \
src/pcm_format.c \
src/pcm_pack.c \
@@ -1052,7 +1078,7 @@ test_run_output_SOURCES = test/run_output.c \
src/audio_parser.c \
src/timer.c \
src/tag.c src/tag_pool.c \
src/fifo_buffer.c \
src/fifo_buffer.c src/growing_fifo.c \
src/page.c \
src/socket_util.c \
src/output_init.c src/output_list.c \
@@ -1069,6 +1095,7 @@ test_run_output_SOURCES = test/run_output.c \
src/filter/normalize_filter_plugin.c \
src/filter/volume_filter_plugin.c \
src/pcm_volume.c \
src/pcm_buffer.c \
src/AudioCompress/compress.c \
src/replay_gain_info.c \
src/replay_gain_config.c \
@@ -1087,6 +1114,7 @@ test_read_mixer_SOURCES = test/read_mixer.c \
src/filter_plugin.c \
src/filter/volume_filter_plugin.c \
src/fd_util.c \
src/pcm_buffer.c \
$(MIXER_SRC)
if ENABLE_BZIP2_TEST

47
NEWS

@@ -1,4 +1,49 @@
ver 0.16.5 (2010/10/09)
ver 0.16.8 (2012/04/04)
* fix for libsamplerate assertion failure
* decoder:
- vorbis (and others): fix seeking at startup
- ffmpeg: read the "year" tag
* encoder:
- vorbis: generate end-of-stream packet before tag
- vorbis: generate end-of-stream packet when playback ends
* output:
- jack: check for connection failure before starting playback
- jack: workaround for libjack1 crash bug
- osx: fix stuttering due to buffering bug
* fix endless loop in text file reader
* update: skip symlinks in path that is to be updated
ver 0.16.7 (2012/02/04)
* input:
- ffmpeg: support libavformat 0.7
* decoder:
- ffmpeg: support libavformat 0.8, libavcodec 0.9
- ffmpeg: support all MPD tags
* output:
- httpd: fix excessive buffering
- openal: force 16 bit playback, as 8 bit doesn't work
- osx: remove sleep call from render callback
- osx: clear render buffer when there's not enough data
* fix moving after current song
ver 0.16.6 (2011/12/01)
* decoder:
- fix assertion failure when resuming streams
- ffmpeg: work around bogus channel count
* encoder:
- flac, null, wave: fix buffer corruption bug
- wave: support packed 24 bit samples
* mapper: fix the bogus "not a directory" error message
* mapper: check "x" and "r" permissions on music directory
* log: print reason for failure
* event_pipe: fix WIN32 regression
* define WINVER in ./configure
* WIN32: autodetect filesystem encoding
ver 0.16.5 (2011/10/09)
* configure.ac
- disable assertions in the non-debugging build
- show solaris plugin result correctly

@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.16.5, musicpd-dev-team@lists.sourceforge.net)
AC_INIT(mpd, 0.16.8, musicpd-dev-team@lists.sourceforge.net)
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
AM_CONFIG_HEADER(config.h)
@@ -68,6 +68,7 @@ AC_CANONICAL_HOST
case "$host_os" in
mingw32* | windows*)
AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0501"
MPD_LIBS="$MPD_LIBS -lws2_32"
;;
esac
@@ -431,6 +432,11 @@ dnl ---------------------------------------------------------------------------
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12 gthread-2.0],,
[AC_MSG_ERROR([GLib 2.12 is required])])
if test x$GCC = xyes; then
# suppress warnings in the GLib headers
GLIB_CFLAGS=`echo $GLIB_CFLAGS |sed -e 's,-I/,-isystem /,g'`
fi
dnl ---------------------------------------------------------------------------
dnl Protocol Options
dnl ---------------------------------------------------------------------------
@@ -445,7 +451,11 @@ if test x$enable_ipv6 = xyes; then
AC_EGREP_CPP([AP_maGiC_VALUE],
[
#include <sys/types.h>
#ifdef WIN32
#include <winsock2.h>
#else
#include <sys/socket.h>
#endif
#include <netdb.h>
#ifdef PF_INET6
#ifdef AF_INET6
@@ -1486,7 +1496,6 @@ if test x$GCC = xyes
then
MPD_CHECK_FLAG([-Wall])
MPD_CHECK_FLAG([-Wextra])
MPD_CHECK_FLAG([-Wno-deprecated-declarations])
MPD_CHECK_FLAG([-Wmissing-prototypes])
MPD_CHECK_FLAG([-Wshadow])
MPD_CHECK_FLAG([-Wpointer-arith])

@@ -194,8 +194,6 @@ parse_cmdline(int argc, char **argv, struct options *options,
if(g_file_test(system_path,
G_FILE_TEST_IS_REGULAR)) {
ret = config_read_file(system_path,error_r);
g_free(system_path);
g_free(&system_config_dirs);
break;
}
++i;;

@@ -64,14 +64,14 @@ audiofile_file_read(AFvirtualfile *vfile, void *data, size_t length)
return nbytes;
}
static long
static AFfileoffset
audiofile_file_length(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
return is->size;
}
static long
static AFfileoffset
audiofile_file_tell(AFvirtualfile *vfile)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
@@ -86,8 +86,8 @@ audiofile_file_destroy(AFvirtualfile *vfile)
vfile->closure = NULL;
}
static long
audiofile_file_seek(AFvirtualfile *vfile, long offset, int is_relative)
static AFfileoffset
audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative)
{
struct input_stream *is = (struct input_stream *) vfile->closure;
int whence = (is_relative ? SEEK_CUR : SEEK_SET);

@@ -40,7 +40,12 @@
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/avutil.h>
#include <libavutil/log.h>
#include <libavutil/mathematics.h>
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
#include <libavutil/dict.h>
#endif
#endif
#undef G_LOG_DOMAIN
@@ -126,11 +131,19 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
stream->decoder = decoder;
stream->input = input;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
stream->io = avio_alloc_context(stream->buffer, sizeof(stream->buffer),
false, stream,
mpd_ffmpeg_stream_read, NULL,
input->seekable
? mpd_ffmpeg_stream_seek : NULL);
#else
stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
false, stream,
mpd_ffmpeg_stream_read, NULL,
input->seekable
? mpd_ffmpeg_stream_seek : NULL);
#endif
if (stream->io == NULL) {
g_free(stream);
return NULL;
@@ -199,6 +212,7 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
return -1;
}
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,25,0)
/**
* On some platforms, libavcodec wants the output buffer aligned to 16
* bytes (because it uses SSE/Altivec internally). This function
@@ -213,6 +227,7 @@ align16(void *p, size_t *length_p)
*length_p -= add;
return (char *)p + add;
}
#endif
G_GNUC_CONST
static double
@@ -232,6 +247,40 @@ time_to_ffmpeg(double t, const AVRational time_base)
time_base);
}
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
/**
* Copy PCM data from a AVFrame to an interleaved buffer.
*/
static int
copy_interleave_frame(const AVCodecContext *codec_context,
const AVFrame *frame,
uint8_t *buffer, size_t buffer_size)
{
int plane_size;
const int data_size =
av_samples_get_buffer_size(&plane_size,
codec_context->channels,
frame->nb_samples,
codec_context->sample_fmt, 1);
if (buffer_size < (size_t)data_size)
/* buffer is too small - shouldn't happen */
return AVERROR(EINVAL);
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
codec_context->channels > 1) {
for (int i = 0, channels = codec_context->channels;
i < channels; i++) {
memcpy(buffer, frame->extended_data[i], plane_size);
buffer += plane_size;
}
} else {
memcpy(buffer, frame->extended_data[0], data_size);
}
return data_size;
}
#endif
static enum decoder_command
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
const AVPacket *packet,
@@ -249,9 +298,15 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
int packet_size = packet->size;
#endif
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
uint8_t aligned_buffer[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
const size_t buffer_size = sizeof(aligned_buffer);
#else
/* libavcodec < 0.8 needs an aligned buffer */
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
size_t buffer_size = sizeof(audio_buf);
int16_t *aligned_buffer = align16(audio_buf, &buffer_size);
#endif
enum decoder_command cmd = DECODE_COMMAND_NONE;
while (
@@ -262,7 +317,22 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
#endif
cmd == DECODE_COMMAND_NONE) {
int audio_size = buffer_size;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
AVFrame frame;
int got_frame = 0;
int len = avcodec_decode_audio4(codec_context,
&frame, &got_frame,
&packet2);
if (len >= 0 && got_frame) {
audio_size = copy_interleave_frame(codec_context,
&frame,
aligned_buffer,
buffer_size);
if (audio_size < 0)
len = audio_size;
} else if (len >= 0)
len = -1;
#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
int len = avcodec_decode_audio3(codec_context,
aligned_buffer, &audio_size,
&packet2);
@@ -301,10 +371,18 @@ ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
{
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(51, 41, 0)
switch (codec_context->sample_fmt) {
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S16:
#else
case SAMPLE_FMT_S16:
#endif
return SAMPLE_FORMAT_S16;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
case AV_SAMPLE_FMT_S32:
#else
case SAMPLE_FMT_S32:
#endif
return SAMPLE_FORMAT_S32;
default:
@@ -377,9 +455,19 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
return;
}
if (av_find_stream_info(format_context)<0) {
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
const int find_result =
avformat_find_stream_info(format_context, NULL);
#else
const int find_result = av_find_stream_info(format_context);
#endif
if (find_result < 0) {
g_warning("Couldn't find stream info\n");
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&format_context);
#else
av_close_input_stream(format_context);
#endif
mpd_ffmpeg_stream_close(stream);
return;
}
@@ -387,7 +475,11 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
int audio_stream = ffmpeg_find_audio_stream(format_context);
if (audio_stream == -1) {
g_warning("No audio stream inside\n");
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&format_context);
#else
av_close_input_stream(format_context);
#endif
mpd_ffmpeg_stream_close(stream);
return;
}
@@ -402,14 +494,11 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
if (!codec) {
g_warning("Unsupported audio codec\n");
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&format_context);
#else
av_close_input_stream(format_context);
mpd_ffmpeg_stream_close(stream);
return;
}
if (avcodec_open(codec_context, codec)<0) {
g_warning("Could not open codec\n");
av_close_input_stream(format_context);
#endif
mpd_ffmpeg_stream_close(stream);
return;
}
@@ -422,8 +511,32 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
codec_context->channels, &error)) {
g_warning("%s", error->message);
g_error_free(error);
avcodec_close(codec_context);
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&format_context);
#else
av_close_input_stream(format_context);
#endif
mpd_ffmpeg_stream_close(stream);
return;
}
/* the audio format must be read from AVCodecContext by now,
because avcodec_open() has been demonstrated to fill bogus
values into AVCodecContext.channels - a change that will be
reverted later by avcodec_decode_audio3() */
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,6,0)
const int open_result = avcodec_open2(codec_context, codec, NULL);
#else
const int open_result = avcodec_open(codec_context, codec);
#endif
if (open_result < 0) {
g_warning("Could not open codec\n");
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&format_context);
#else
av_close_input_stream(format_context);
#endif
mpd_ffmpeg_stream_close(stream);
return;
}
@@ -467,7 +580,11 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
} while (cmd != DECODE_COMMAND_STOP);
avcodec_close(codec_context);
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&format_context);
#else
av_close_input_stream(format_context);
#endif
mpd_ffmpeg_stream_close(stream);
}
@@ -478,48 +595,44 @@ typedef struct ffmpeg_tag_map {
} ffmpeg_tag_map;
static const ffmpeg_tag_map ffmpeg_tag_maps[] = {
{ TAG_TITLE, "title" },
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(50<<8))
{ TAG_ARTIST, "artist" },
{ TAG_DATE, "date" },
#else
#if LIBAVFORMAT_VERSION_INT < ((52<<16)+(50<<8))
{ TAG_ARTIST, "author" },
{ TAG_DATE, "year" },
#endif
{ TAG_ALBUM, "album" },
{ TAG_COMMENT, "comment" },
{ TAG_GENRE, "genre" },
{ TAG_TRACK, "track" },
{ TAG_DATE, "year" },
{ TAG_ARTIST_SORT, "author-sort" },
{ TAG_ALBUM_ARTIST, "album_artist" },
{ TAG_ALBUM_ARTIST_SORT, "album_artist-sort" },
{ TAG_COMPOSER, "composer" },
{ TAG_PERFORMER, "performer" },
{ TAG_DISC, "disc" },
/* sentinel */
{ TAG_NUM_OF_ITEM_TYPES, NULL }
};
static bool
ffmpeg_copy_metadata(struct tag *tag,
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0)
AVDictionary *m,
#else
AVMetadata *m,
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,1,0)
#define AVDictionary AVMetadata
#define AVDictionaryEntry AVMetadataTag
#define av_dict_get av_metadata_get
#endif
const ffmpeg_tag_map tag_map)
static void
ffmpeg_copy_metadata(struct tag *tag, enum tag_type type,
AVDictionary *m, const char *name)
{
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0)
AVDictionaryEntry *mt = NULL;
while ((mt = av_dict_get(m, tag_map.name, mt, 0)) != NULL)
tag_add_item(tag, tag_map.type, mt->value);
#else
AVMetadataTag *mt = NULL;
while ((mt = av_dict_get(m, name, mt, 0)) != NULL)
tag_add_item(tag, type, mt->value);
}
while ((mt = av_metadata_get(m, tag_map.name, mt, 0)) != NULL)
tag_add_item(tag, tag_map.type, mt->value);
#endif
static void
ffmpeg_copy_dictionary(struct tag *tag, AVDictionary *dict)
{
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
ffmpeg_copy_metadata(tag, i,
dict, tag_item_names[i]);
return mt != NULL;
for (const struct ffmpeg_tag_map *i = ffmpeg_tag_maps;
i->name != NULL; ++i)
ffmpeg_copy_metadata(tag, i->type, dict, i->name);
}
#endif
@@ -543,8 +656,18 @@ ffmpeg_stream_tag(struct input_stream *is)
return NULL;
}
if (av_find_stream_info(f) < 0) {
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
const int find_result =
avformat_find_stream_info(f, NULL);
#else
const int find_result = av_find_stream_info(f);
#endif
if (find_result < 0) {
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&f);
#else
av_close_input_stream(f);
#endif
mpd_ffmpeg_stream_close(stream);
return NULL;
}
@@ -556,14 +679,14 @@ ffmpeg_stream_tag(struct input_stream *is)
: 0;
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
#endif
for (unsigned i = 0; i < sizeof(ffmpeg_tag_maps)/sizeof(ffmpeg_tag_map); i++) {
int idx = ffmpeg_find_audio_stream(f);
ffmpeg_copy_metadata(tag, f->metadata, ffmpeg_tag_maps[i]);
if (idx >= 0)
ffmpeg_copy_metadata(tag, f->streams[idx]->metadata, ffmpeg_tag_maps[i]);
}
ffmpeg_copy_dictionary(tag, f->metadata);
int idx = ffmpeg_find_audio_stream(f);
if (idx >= 0)
ffmpeg_copy_dictionary(tag, f->streams[idx]->metadata);
#else
if (f->author[0])
tag_add_item(tag, TAG_ARTIST, f->author);
@@ -590,7 +713,11 @@ ffmpeg_stream_tag(struct input_stream *is)
#endif
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
avformat_close_input(&f);
#else
av_close_input_stream(f);
#endif
mpd_ffmpeg_stream_close(stream);
return tag;

@@ -94,6 +94,12 @@ mp4_read(void *user_data, void *buffer, uint32_t length)
{
struct mp4ff_input_stream *mis = user_data;
if (length == 0)
/* libmp4ff is known to attempt to read 0 bytes - make
this a special case, because the input_stream API
would not allow this */
return 0;
return decoder_read(mis->decoder, mis->input_stream, buffer, length);
}

@@ -89,12 +89,24 @@ decoder_prepare_initial_seek(struct decoder *decoder)
const struct decoder_control *dc = decoder->dc;
assert(dc->pipe != NULL);
if (dc->state != DECODE_STATE_DECODE)
/* wait until the decoder has finished initialisation
(reading file headers etc.) before emitting the
virtual "SEEK" command */
return false;
if (decoder->initial_seek_running)
/* initial seek has already begun - override any other
command */
return true;
if (decoder->initial_seek_pending) {
if (!dc->seekable) {
/* seeking is not possible */
decoder->initial_seek_pending = false;
return false;
}
if (dc->command == DECODE_COMMAND_NONE) {
/* begin initial seek */

@@ -38,6 +38,7 @@
#include <glib.h>
#include <unistd.h>
#include <stdio.h> /* for SEEK_SET */
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "decoder_thread"

@@ -68,7 +68,15 @@ directory_free(struct directory *directory)
const char *
directory_get_name(const struct directory *directory)
{
return g_basename(directory->path);
assert(!directory_is_root(directory));
assert(directory->path != NULL);
const char *slash = strrchr(directory->path, '/');
assert((slash == NULL) == directory_is_root(directory->parent));
return slash != NULL
? slash + 1
: directory->path;
}
void

@@ -43,7 +43,7 @@ struct directory {
time_t mtime;
ino_t inode;
dev_t device;
unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */
bool have_stat; /* not needed if ino_t == dev_t == 0 is impossible */
char path[sizeof(long)];
};

@@ -22,6 +22,8 @@
#include "encoder_plugin.h"
#include "audio_format.h"
#include "pcm_buffer.h"
#include "fifo_buffer.h"
#include "growing_fifo.h"
#include <assert.h>
#include <string.h>
@@ -38,8 +40,11 @@ struct flac_encoder {
struct pcm_buffer expand_buffer;
struct pcm_buffer buffer;
size_t buffer_length;
/**
* This buffer will hold encoded data from libFLAC until it is
* picked up with flac_encoder_read().
*/
struct fifo_buffer *output_buffer;
};
extern const struct encoder_plugin flac_encoder_plugin;
@@ -140,11 +145,8 @@ flac_write_callback(G_GNUC_UNUSED const FLAC__StreamEncoder *fse,
{
struct flac_encoder *encoder = (struct flac_encoder *) client_data;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + bytes);
//transfer data to buffer
memcpy( buffer + encoder->buffer_length, data, bytes);
encoder->buffer_length += bytes;
growing_fifo_append(&encoder->output_buffer, data, bytes);
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
@@ -156,8 +158,8 @@ flac_encoder_close(struct encoder *_encoder)
FLAC__stream_encoder_delete(encoder->fse);
pcm_buffer_deinit(&encoder->buffer);
pcm_buffer_deinit(&encoder->expand_buffer);
fifo_buffer_free(encoder->output_buffer);
}
static bool
@@ -201,10 +203,10 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
return false;
}
encoder->buffer_length = 0;
pcm_buffer_init(&encoder->buffer);
pcm_buffer_init(&encoder->expand_buffer);
encoder->output_buffer = growing_fifo_new();
/* this immediatelly outputs data throught callback */
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
@@ -325,16 +327,18 @@ static size_t
flac_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length);
if (length > encoder->buffer_length)
length = encoder->buffer_length;
size_t max_length;
const char *src = fifo_buffer_read(encoder->output_buffer,
&max_length);
if (src == NULL)
return 0;
memcpy(dest, buffer, length);
encoder->buffer_length -= length;
memmove(buffer, buffer + length, encoder->buffer_length);
if (length > max_length)
length = max_length;
memcpy(dest, src, length);
fifo_buffer_consume(encoder->output_buffer, length);
return length;
}
@@ -350,6 +354,7 @@ const struct encoder_plugin flac_encoder_plugin = {
.finish = flac_encoder_finish,
.open = flac_encoder_open,
.close = flac_encoder_close,
.end = flac_encoder_flush,
.flush = flac_encoder_flush,
.write = flac_encoder_write,
.read = flac_encoder_read,

@@ -20,7 +20,8 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
#include "pcm_buffer.h"
#include "fifo_buffer.h"
#include "growing_fifo.h"
#include <assert.h>
#include <string.h>
@@ -28,8 +29,7 @@
struct null_encoder {
struct encoder encoder;
struct pcm_buffer buffer;
size_t buffer_length;
struct fifo_buffer *buffer;
};
extern const struct encoder_plugin null_encoder_plugin;
@@ -65,7 +65,7 @@ null_encoder_close(struct encoder *_encoder)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
pcm_buffer_deinit(&encoder->buffer);
fifo_buffer_free(encoder->buffer);
}
@@ -76,9 +76,7 @@ null_encoder_open(struct encoder *_encoder,
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
encoder->buffer_length = 0;
pcm_buffer_init(&encoder->buffer);
encoder->buffer = growing_fifo_new();
return true;
}
@@ -88,28 +86,26 @@ null_encoder_write(struct encoder *_encoder,
G_GNUC_UNUSED GError **error)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + length);
memcpy(buffer+encoder->buffer_length, data, length);
encoder->buffer_length += length;
return true;
growing_fifo_append(&encoder->buffer, data, length);
return length;
}
static size_t
null_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct null_encoder *encoder = (struct null_encoder *)_encoder;
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length);
if (length > encoder->buffer_length)
length = encoder->buffer_length;
size_t max_length;
const void *src = fifo_buffer_read(encoder->buffer, &max_length);
if (src == NULL)
return 0;
memcpy(dest, buffer, length);
encoder->buffer_length -= length;
memmove(buffer, buffer + length, encoder->buffer_length);
if (length > max_length)
length = max_length;
memcpy(dest, src, length);
fifo_buffer_consume(encoder->buffer, length);
return length;
}

@@ -300,6 +300,7 @@ const struct encoder_plugin twolame_encoder_plugin = {
.finish = twolame_encoder_finish,
.open = twolame_encoder_open,
.close = twolame_encoder_close,
.end = twolame_encoder_flush,
.flush = twolame_encoder_flush,
.write = twolame_encoder_write,
.read = twolame_encoder_read,

@@ -285,8 +285,6 @@ vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
vorbis_analysis_init(&encoder->vd, &encoder->vi);
vorbis_block_init(&encoder->vd, &encoder->vb);
ogg_stream_reset(&encoder->os);
encoder->flush = true;
return true;
}
@@ -407,6 +405,7 @@ const struct encoder_plugin vorbis_encoder_plugin = {
.finish = vorbis_encoder_finish,
.open = vorbis_encoder_open,
.close = vorbis_encoder_close,
.end = vorbis_encoder_pre_tag,
.flush = vorbis_encoder_flush,
.pre_tag = vorbis_encoder_pre_tag,
.tag = vorbis_encoder_tag,

@@ -20,7 +20,8 @@
#include "config.h"
#include "encoder_api.h"
#include "encoder_plugin.h"
#include "pcm_buffer.h"
#include "fifo_buffer.h"
#include "growing_fifo.h"
#include <assert.h>
#include <string.h>
@@ -29,8 +30,7 @@ struct wave_encoder {
struct encoder encoder;
unsigned bits;
struct pcm_buffer buffer;
size_t buffer_length;
struct fifo_buffer *buffer;
};
struct wave_header {
@@ -92,7 +92,6 @@ wave_encoder_init(G_GNUC_UNUSED const struct config_param *param,
encoder = g_new(struct wave_encoder, 1);
encoder_struct_init(&encoder->encoder, &wave_encoder_plugin);
pcm_buffer_init(&encoder->buffer);
return &encoder->encoder;
}
@@ -102,7 +101,6 @@ wave_encoder_finish(struct encoder *_encoder)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
pcm_buffer_deinit(&encoder->buffer);
g_free(encoder);
}
@@ -112,7 +110,6 @@ wave_encoder_open(struct encoder *_encoder,
G_GNUC_UNUSED GError **error)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
void *buffer;
assert(audio_format_valid(audio_format));
@@ -125,6 +122,11 @@ wave_encoder_open(struct encoder *_encoder,
encoder->bits = 16;
break;
case SAMPLE_FORMAT_S24:
audio_format->format = SAMPLE_FORMAT_S24_P32;
encoder->bits = 24;
break;
case SAMPLE_FORMAT_S24_P32:
encoder->bits = 24;
break;
@@ -139,19 +141,29 @@ wave_encoder_open(struct encoder *_encoder,
break;
}
buffer = pcm_buffer_get(&encoder->buffer, sizeof(struct wave_header) );
encoder->buffer = growing_fifo_new();
struct wave_header *header =
growing_fifo_write(&encoder->buffer, sizeof(*header));
/* create PCM wave header in initial buffer */
fill_wave_header((struct wave_header *) buffer,
fill_wave_header(header,
audio_format->channels,
encoder->bits,
audio_format->sample_rate,
(encoder->bits / 8) * audio_format->channels );
fifo_buffer_append(encoder->buffer, sizeof(*header));
encoder->buffer_length = sizeof(struct wave_header);
return true;
}
static void
wave_encoder_close(struct encoder *_encoder)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
fifo_buffer_free(encoder->buffer);
}
static inline size_t
pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
{
@@ -198,9 +210,8 @@ wave_encoder_write(struct encoder *_encoder,
G_GNUC_UNUSED GError **error)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
void *dst;
dst = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + length);
void *dst = growing_fifo_write(&encoder->buffer, length);
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
switch (encoder->bits) {
@@ -232,7 +243,7 @@ wave_encoder_write(struct encoder *_encoder,
#error G_BYTE_ORDER set to G_PDP_ENDIAN is not supported by wave_encoder
#endif
encoder->buffer_length += length;
fifo_buffer_append(encoder->buffer, length);
return true;
}
@@ -240,16 +251,17 @@ static size_t
wave_encoder_read(struct encoder *_encoder, void *dest, size_t length)
{
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
uint8_t *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length );
if (length > encoder->buffer_length)
length = encoder->buffer_length;
size_t max_length;
const void *src = fifo_buffer_read(encoder->buffer, &max_length);
if (src == NULL)
return 0;
memcpy(dest, buffer, length);
encoder->buffer_length -= length;
memmove(buffer, buffer + length, encoder->buffer_length);
if (length > max_length)
length = max_length;
memcpy(dest, src, length);
fifo_buffer_consume(encoder->buffer, length);
return length;
}
@@ -264,6 +276,7 @@ const struct encoder_plugin wave_encoder_plugin = {
.init = wave_encoder_init,
.finish = wave_encoder_finish,
.open = wave_encoder_open,
.close = wave_encoder_close,
.write = wave_encoder_write,
.read = wave_encoder_read,
.get_mime_type = wave_encoder_get_mime_type,

@@ -22,6 +22,7 @@
#include <glib.h>
#include <assert.h>
#include <stdbool.h>
#include <stddef.h>
@@ -32,6 +33,10 @@ struct tag;
struct encoder {
const struct encoder_plugin *plugin;
#ifndef NDEBUG
bool open, pre_tag, tag, end;
#endif
};
struct encoder_plugin {
@@ -48,6 +53,8 @@ struct encoder_plugin {
void (*close)(struct encoder *encoder);
bool (*end)(struct encoder *encoder, GError **error);
bool (*flush)(struct encoder *encoder, GError **error);
bool (*pre_tag)(struct encoder *encoder, GError **error);
@@ -73,6 +80,10 @@ encoder_struct_init(struct encoder *encoder,
const struct encoder_plugin *plugin)
{
encoder->plugin = plugin;
#ifndef NDEBUG
encoder->open = false;
#endif
}
/**
@@ -98,6 +109,8 @@ encoder_init(const struct encoder_plugin *plugin,
static inline void
encoder_finish(struct encoder *encoder)
{
assert(!encoder->open);
encoder->plugin->finish(encoder);
}
@@ -116,7 +129,14 @@ static inline bool
encoder_open(struct encoder *encoder, struct audio_format *audio_format,
GError **error)
{
return encoder->plugin->open(encoder, audio_format, error);
assert(!encoder->open);
bool success = encoder->plugin->open(encoder, audio_format, error);
#ifndef NDEBUG
encoder->open = success;
encoder->pre_tag = encoder->tag = encoder->end = false;
#endif
return success;
}
/**
@@ -128,8 +148,43 @@ encoder_open(struct encoder *encoder, struct audio_format *audio_format,
static inline void
encoder_close(struct encoder *encoder)
{
assert(encoder->open);
if (encoder->plugin->close != NULL)
encoder->plugin->close(encoder);
#ifndef NDEBUG
encoder->open = false;
#endif
}
/**
* Ends the stream: flushes the encoder object, generate an
* end-of-stream marker (if applicable), make everything which might
* currently be buffered available by encoder_read().
*
* After this function has been called, the encoder may not be usable
* for more data, and only encoder_read() and encoder_close() can be
* called.
*
* @param encoder the encoder
* @param error location to store the error occuring, or NULL to ignore errors.
* @return true on success
*/
static inline bool
encoder_end(struct encoder *encoder, GError **error)
{
assert(encoder->open);
assert(!encoder->end);
#ifndef NDEBUG
encoder->end = true;
#endif
/* this method is optional */
return encoder->plugin->end != NULL
? encoder->plugin->end(encoder, error)
: true;
}
/**
@@ -143,6 +198,11 @@ encoder_close(struct encoder *encoder)
static inline bool
encoder_flush(struct encoder *encoder, GError **error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
/* this method is optional */
return encoder->plugin->flush != NULL
? encoder->plugin->flush(encoder, error)
@@ -162,10 +222,20 @@ encoder_flush(struct encoder *encoder, GError **error)
static inline bool
encoder_pre_tag(struct encoder *encoder, GError **error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
/* this method is optional */
return encoder->plugin->pre_tag != NULL
bool success = encoder->plugin->pre_tag != NULL
? encoder->plugin->pre_tag(encoder, error)
: true;
#ifndef NDEBUG
encoder->pre_tag = success;
#endif
return success;
}
/**
@@ -182,6 +252,15 @@ encoder_pre_tag(struct encoder *encoder, GError **error)
static inline bool
encoder_tag(struct encoder *encoder, const struct tag *tag, GError **error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(encoder->tag);
assert(!encoder->end);
#ifndef NDEBUG
encoder->tag = false;
#endif
/* this method is optional */
return encoder->plugin->tag != NULL
? encoder->plugin->tag(encoder, tag, error)
@@ -201,6 +280,11 @@ static inline bool
encoder_write(struct encoder *encoder, const void *data, size_t length,
GError **error)
{
assert(encoder->open);
assert(!encoder->pre_tag);
assert(!encoder->tag);
assert(!encoder->end);
return encoder->plugin->write(encoder, data, length, error);
}
@@ -215,6 +299,16 @@ encoder_write(struct encoder *encoder, const void *data, size_t length,
static inline size_t
encoder_read(struct encoder *encoder, void *dest, size_t length)
{
assert(encoder->open);
assert(!encoder->pre_tag || !encoder->tag);
#ifndef NDEBUG
if (encoder->pre_tag) {
encoder->pre_tag = false;
encoder->tag = true;
}
#endif
return encoder->plugin->read(encoder, dest, length);
}

@@ -21,7 +21,6 @@
#include "event_pipe.h"
#include "fd_util.h"
#include "mpd_error.h"
#include "glib_socket.h"
#include <stdbool.h>
#include <assert.h>
@@ -95,7 +94,11 @@ void event_pipe_init(void)
if (ret < 0)
MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
channel = g_io_channel_new_socket(event_pipe[0]);
#ifndef G_OS_WIN32
channel = g_io_channel_unix_new(event_pipe[0]);
#else
channel = g_io_channel_win32_new_fd(event_pipe[0]);
#endif
g_io_channel_set_encoding(channel, NULL, NULL);
g_io_channel_set_buffered(channel, false);
@@ -156,5 +159,6 @@ void event_pipe_emit_fast(enum pipe_event event)
assert((unsigned)event < PIPE_EVENT_MAX);
pipe_events[event] = true;
(void)write(event_pipe[1], "", 1);
G_GNUC_UNUSED ssize_t nbytes = write(event_pipe[1], "", 1);
}

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
@@ -58,6 +58,39 @@ fifo_buffer_new(size_t size)
return buffer;
}
static void
fifo_buffer_move(struct fifo_buffer *buffer);
struct fifo_buffer *
fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size)
{
if (buffer == NULL)
return new_size > 0
? fifo_buffer_new(new_size)
: NULL;
/* existing data must fit in new size */
assert(new_size >= buffer->end - buffer->start);
if (new_size == 0) {
fifo_buffer_free(buffer);
return NULL;
}
/* compress the buffer when we're shrinking and the tail of
the buffer would exceed the new size */
if (buffer->end > new_size)
fifo_buffer_move(buffer);
/* existing data must fit in new size: second check */
assert(buffer->end <= new_size);
buffer = g_realloc(buffer, sizeof(*buffer) - sizeof(buffer->buffer) +
new_size);
buffer->size = new_size;
return buffer;
}
void
fifo_buffer_free(struct fifo_buffer *buffer)
{
@@ -66,6 +99,22 @@ fifo_buffer_free(struct fifo_buffer *buffer)
g_free(buffer);
}
size_t
fifo_buffer_capacity(const struct fifo_buffer *buffer)
{
assert(buffer != NULL);
return buffer->size;
}
size_t
fifo_buffer_available(const struct fifo_buffer *buffer)
{
assert(buffer != NULL);
return buffer->end - buffer->start;
}
void
fifo_buffer_clear(struct fifo_buffer *buffer)
{

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2003-2010 The Music Player Daemon Project
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
@@ -56,12 +56,37 @@ struct fifo_buffer;
struct fifo_buffer *
fifo_buffer_new(size_t size);
/**
* Change the capacity of the #fifo_buffer, while preserving existing
* data.
*
* @param buffer the old buffer, may be NULL
* @param new_size the requested new size of the #fifo_buffer; must
* not be smaller than the data which is stored in the old buffer
* @return the new buffer, may be NULL if the requested new size is 0
*/
struct fifo_buffer *
fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size);
/**
* Frees the resources consumed by this #fifo_buffer object.
*/
void
fifo_buffer_free(struct fifo_buffer *buffer);
/**
* Return the capacity of the buffer, i.e. the size that was passed to
* fifo_buffer_new().
*/
size_t
fifo_buffer_capacity(const struct fifo_buffer *buffer);
/**
* Return the number of bytes currently stored in the buffer.
*/
size_t
fifo_buffer_available(const struct fifo_buffer *buffer);
/**
* Clears all data currently in this #fifo_buffer object. This does
* not overwrite the actuall buffer; it just resets the internal

90
src/growing_fifo.c Normal file

@@ -0,0 +1,90 @@
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "growing_fifo.h"
#include "fifo_buffer.h"
#include <assert.h>
#include <string.h>
/**
* Align buffer sizes at 8 kB boundaries. Must be a power of two.
*/
static const size_t GROWING_FIFO_ALIGN = 8192;
/**
* Align the specified size to the next #GROWING_FIFO_ALIGN boundary.
*/
static size_t
align(size_t size)
{
return ((size - 1) | (GROWING_FIFO_ALIGN - 1)) + 1;
}
struct fifo_buffer *
growing_fifo_new(void)
{
return fifo_buffer_new(GROWING_FIFO_ALIGN);
}
void *
growing_fifo_write(struct fifo_buffer **buffer_p, size_t length)
{
assert(buffer_p != NULL);
struct fifo_buffer *buffer = *buffer_p;
assert(buffer != NULL);
size_t max_length;
void *p = fifo_buffer_write(buffer, &max_length);
if (p != NULL && max_length >= length)
return p;
/* grow */
size_t new_size = fifo_buffer_available(buffer) + length;
assert(new_size > fifo_buffer_capacity(buffer));
*buffer_p = buffer = fifo_buffer_realloc(buffer, align(new_size));
/* try again */
p = fifo_buffer_write(buffer, &max_length);
assert(p != NULL);
assert(max_length >= length);
return p;
}
void
growing_fifo_append(struct fifo_buffer **buffer_p,
const void *data, size_t length)
{
void *p = growing_fifo_write(buffer_p, length);
memcpy(p, data, length);
fifo_buffer_append(*buffer_p, length);
}

73
src/growing_fifo.h Normal file

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** \file
*
* Helper functions for our FIFO buffer library (fifo_buffer.h) that
* allows growing the buffer on demand.
*
* This library is not thread safe.
*/
#ifndef MPD_GROWING_FIFO_H
#define MPD_GROWING_FIFO_H
#include <stddef.h>
struct fifo_buffer;
/**
* Allocate a new #fifo_buffer with the default size.
*/
struct fifo_buffer *
growing_fifo_new(void);
/**
* Prepares writing to the buffer, see fifo_buffer_write() for
* details. The difference is that this function will automatically
* grow the buffer if it is too small.
*
* The caller is responsible for limiting the capacity of the buffer.
*
* @param length the number of bytes that will be written
* @return a pointer to the end of the buffer (will not be NULL)
*/
void *
growing_fifo_write(struct fifo_buffer **buffer_p, size_t length);
/**
* A helper function that combines growing_fifo_write(), memcpy(),
* fifo_buffer_append().
*/
void
growing_fifo_append(struct fifo_buffer **buffer_p,
const void *data, size_t length);
#endif

@@ -32,10 +32,18 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_ffmpeg"
#ifndef AV_VERSION_INT
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
#endif
struct input_ffmpeg {
struct input_stream base;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
AVIOContext *h;
#else
URLContext *h;
#endif
bool eof;
};
@@ -46,6 +54,17 @@ ffmpeg_quark(void)
return g_quark_from_static_string("ffmpeg");
}
static inline bool
input_ffmpeg_supported(void)
{
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
void *opaque = NULL;
return avio_enum_protocols(&opaque, 0) != NULL;
#else
return av_protocol_next(NULL) != NULL;
#endif
}
static bool
input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param,
G_GNUC_UNUSED GError **error_r)
@@ -54,7 +73,7 @@ input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param,
#if LIBAVFORMAT_VERSION_MAJOR >= 52
/* disable this plugin if there's no registered protocol */
if (av_protocol_next(NULL) == NULL) {
if (!input_ffmpeg_supported()) {
g_set_error(error_r, ffmpeg_quark(), 0,
"No protocol");
return false;
@@ -80,7 +99,11 @@ input_ffmpeg_open(const char *uri, GError **error_r)
i = g_new(struct input_ffmpeg, 1);
input_stream_init(&i->base, &input_plugin_ffmpeg, uri);
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
int ret = avio_open(&i->h, uri, AVIO_FLAG_READ);
#else
int ret = url_open(&i->h, uri, URL_RDONLY);
#endif
if (ret != 0) {
g_free(i);
g_set_error(error_r, ffmpeg_quark(), ret,
@@ -91,8 +114,13 @@ input_ffmpeg_open(const char *uri, GError **error_r)
i->eof = false;
i->base.ready = true;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
i->base.seekable = (i->h->seekable & AVIO_SEEKABLE_NORMAL) != 0;
i->base.size = avio_size(i->h);
#else
i->base.seekable = !i->h->is_streamed;
i->base.size = url_filesize(i->h);
#endif
/* hack to make MPD select the "ffmpeg" decoder plugin - since
avio.h doesn't tell us the MIME type of the resource, we
@@ -109,7 +137,11 @@ input_ffmpeg_read(struct input_stream *is, void *ptr, size_t size,
{
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
int ret = avio_read(i->h, ptr, size);
#else
int ret = url_read(i->h, ptr, size);
#endif
if (ret <= 0) {
if (ret < 0)
g_set_error(error_r, ffmpeg_quark(), 0,
@@ -128,7 +160,11 @@ input_ffmpeg_close(struct input_stream *is)
{
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
avio_close(i->h);
#else
url_close(i->h);
#endif
input_stream_deinit(&i->base);
g_free(i);
}
@@ -146,7 +182,11 @@ input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence,
G_GNUC_UNUSED GError **error_r)
{
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
int64_t ret = avio_seek(i->h, offset, whence);
#else
int64_t ret = url_seek(i->h, offset, whence);
#endif
if (ret >= 0) {
i->eof = false;

@@ -139,8 +139,8 @@ log_init_file(const char *path, unsigned line)
out_filename = path;
out_fd = open_log_file();
if (out_fd < 0)
MPD_ERROR("problem opening log file \"%s\" (config line %u) "
"for writing\n", path, line);
MPD_ERROR("failed to open log file \"%s\" (config line %u): %s",
path, line, g_strerror(errno));
g_log_set_default_handler(file_log_func, NULL);
}

@@ -27,7 +27,6 @@
#include <glib.h>
#define WINVER 0x0501
#include <windows.h>
static int service_argc;

@@ -31,6 +31,10 @@
#include <assert.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <dirent.h>
static char *music_dir;
static size_t music_dir_length;
@@ -51,25 +55,51 @@ strdup_chop_slash(const char *path_fs)
return g_strndup(path_fs, length);
}
static void
check_directory(const char *path)
{
struct stat st;
if (stat(path, &st) < 0) {
g_warning("Failed to stat directory \"%s\": %s",
path, g_strerror(errno));
return;
}
if (!S_ISDIR(st.st_mode)) {
g_warning("Not a directory: %s", path);
return;
}
#ifndef WIN32
char *x = g_build_filename(path, ".", NULL);
if (stat(x, &st) < 0 && errno == EACCES)
g_warning("No permission to traverse (\"execute\") directory: %s",
path);
g_free(x);
#endif
DIR *dir = opendir(path);
if (dir == NULL && errno == EACCES)
g_warning("No permission to read directory: %s", path);
else
closedir(dir);
}
static void
mapper_set_music_dir(const char *path)
{
check_directory(path);
music_dir = strdup_chop_slash(path);
music_dir_length = strlen(music_dir);
if (!g_file_test(music_dir, G_FILE_TEST_IS_DIR))
g_warning("music directory is not a directory: \"%s\"",
music_dir);
}
static void
mapper_set_playlist_dir(const char *path)
{
playlist_dir = g_strdup(path);
check_directory(path);
if (!g_file_test(playlist_dir, G_FILE_TEST_IS_DIR))
g_warning("playlist directory is not a directory: \"%s\"",
playlist_dir);
playlist_dir = g_strdup(path);
}
void mapper_init(const char *_music_dir, const char *_playlist_dir)

@@ -143,6 +143,13 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
for (unsigned i = 0; i < jd->audio_format.channels; ++i) {
out = jack_port_get_buffer(jd->ports[i], nframes);
if (out == NULL)
/* workaround for libjack1 bug: if the server
connection fails, the process callback is
invoked anyway, but unable to get a
buffer */
continue;
jack_ringbuffer_read(jd->ringbuffer[i],
(char *)out, available * jack_sample_size);
@@ -156,6 +163,12 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
for (unsigned i = jd->audio_format.channels;
i < jd->num_source_ports; ++i) {
out = jack_port_get_buffer(jd->ports[i], nframes);
if (out == NULL)
/* workaround for libjack1 bug: if the server
connection fails, the process callback is
invoked anyway, but unable to get a
buffer */
continue;
for (jack_nframes_t f = 0; f < nframes; ++f)
out[f] = 0.0;
@@ -563,6 +576,9 @@ mpd_jack_open(void *data, struct audio_format *audio_format, GError **error_r)
jd->pause = false;
if (jd->client != NULL && jd->shutdown)
mpd_jack_disconnect(jd);
if (jd->client == NULL && !mpd_jack_connect(jd, error_r))
return false;

@@ -58,6 +58,10 @@ openal_output_quark(void)
static ALenum
openal_audio_format(struct audio_format *audio_format)
{
/* note: cannot map SAMPLE_FORMAT_S8 to AL_FORMAT_STEREO8 or
AL_FORMAT_MONO8 since OpenAL expects unsigned 8 bit
samples, while MPD uses signed samples */
switch (audio_format->format) {
case SAMPLE_FORMAT_S16:
if (audio_format->channels == 2)
@@ -69,16 +73,6 @@ openal_audio_format(struct audio_format *audio_format)
audio_format->channels = 1;
return openal_audio_format(audio_format);
case SAMPLE_FORMAT_S8:
if (audio_format->channels == 2)
return AL_FORMAT_STEREO8;
if (audio_format->channels == 1)
return AL_FORMAT_MONO8;
/* fall back to mono */
audio_format->channels = 1;
return openal_audio_format(audio_format);
default:
/* fall back to 16 bit */
audio_format->format = SAMPLE_FORMAT_S16;

@@ -19,6 +19,7 @@
#include "config.h"
#include "output_api.h"
#include "fifo_buffer.h"
#include <glib.h>
#include <AudioUnit/AudioUnit.h>
@@ -31,10 +32,8 @@ struct osx_output {
AudioUnit au;
GMutex *mutex;
GCond *condition;
char *buffer;
size_t buffer_size;
size_t pos;
size_t len;
struct fifo_buffer *buffer;
};
/**
@@ -64,11 +63,6 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
oo->mutex = g_mutex_new();
oo->condition = g_cond_new();
oo->pos = 0;
oo->len = 0;
oo->buffer = NULL;
oo->buffer_size = 0;
return oo;
}
@@ -76,7 +70,6 @@ static void osx_output_finish(void *data)
{
struct osx_output *od = data;
g_free(od->buffer);
g_mutex_free(od->mutex);
g_cond_free(od->condition);
g_free(od);
@@ -87,7 +80,7 @@ static void osx_output_cancel(void *data)
struct osx_output *od = data;
g_mutex_lock(od->mutex);
od->len = 0;
fifo_buffer_clear(od->buffer);
g_mutex_unlock(od->mutex);
}
@@ -98,6 +91,8 @@ static void osx_output_close(void *data)
AudioOutputUnitStop(od->au);
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
fifo_buffer_free(od->buffer);
}
static OSStatus
@@ -111,40 +106,29 @@ osx_render(void *vdata,
struct osx_output *od = (struct osx_output *) vdata;
AudioBuffer *buffer = &buffer_list->mBuffers[0];
size_t buffer_size = buffer->mDataByteSize;
size_t bytes_to_copy;
size_t trailer_length;
size_t dest_pos = 0;
assert(od->buffer != NULL);
g_mutex_lock(od->mutex);
bytes_to_copy = MIN(od->len, buffer_size);
buffer_size = bytes_to_copy;
od->len -= bytes_to_copy;
size_t nbytes;
const void *src = fifo_buffer_read(od->buffer, &nbytes);
trailer_length = od->buffer_size - od->pos;
if (bytes_to_copy > trailer_length) {
memcpy((unsigned char*)buffer->mData + dest_pos,
od->buffer + od->pos, trailer_length);
od->pos = 0;
dest_pos += trailer_length;
bytes_to_copy -= trailer_length;
}
if (src != NULL) {
if (nbytes > buffer_size)
nbytes = buffer_size;
memcpy((unsigned char*)buffer->mData + dest_pos,
od->buffer + od->pos, bytes_to_copy);
od->pos += bytes_to_copy;
if (od->pos >= od->buffer_size)
od->pos = 0;
memcpy(buffer->mData, src, nbytes);
fifo_buffer_consume(od->buffer, nbytes);
} else
nbytes = 0;
g_cond_signal(od->condition);
g_mutex_unlock(od->mutex);
buffer->mDataByteSize = buffer_size;
if (!buffer_size) {
g_usleep(1000);
}
if (nbytes < buffer_size)
memset((unsigned char*)buffer->mData + nbytes, 0,
buffer_size - nbytes);
return 0;
}
@@ -247,15 +231,12 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
}
/* create a buffer of 1s */
od->buffer_size = (audio_format->sample_rate) *
audio_format_frame_size(audio_format);
od->buffer = g_realloc(od->buffer, od->buffer_size);
od->pos = 0;
od->len = 0;
od->buffer = fifo_buffer_new(audio_format->sample_rate *
audio_format_frame_size(audio_format));
status = AudioOutputUnitStart(od->au);
if (status != 0) {
fifo_buffer_free(od->buffer);
g_set_error(error, osx_output_quark(), 0,
"unable to start audio output: %s",
GetMacOSStatusCommentString(status));
@@ -270,33 +251,30 @@ osx_output_play(void *data, const void *chunk, size_t size,
G_GNUC_UNUSED GError **error)
{
struct osx_output *od = data;
size_t start, nbytes;
g_mutex_lock(od->mutex);
while (od->len >= od->buffer_size)
void *dest;
size_t max_length;
while (true) {
dest = fifo_buffer_write(od->buffer, &max_length);
if (dest != NULL)
break;
/* wait for some free space in the buffer */
g_cond_wait(od->condition, od->mutex);
}
start = od->pos + od->len;
if (start >= od->buffer_size)
start -= od->buffer_size;
if (size > max_length)
size = max_length;
nbytes = start < od->pos
? od->pos - start
: od->buffer_size - start;
assert(nbytes > 0);
if (nbytes > size)
nbytes = size;
memcpy(od->buffer + start, chunk, nbytes);
od->len += nbytes;
memcpy(dest, chunk, size);
fifo_buffer_append(od->buffer, size);
g_mutex_unlock(od->mutex);
return nbytes;
return size;
}
const struct audio_output_plugin osxPlugin = {

@@ -191,7 +191,7 @@ recorder_output_close(void *data)
/* flush the encoder and write the rest to the file */
if (encoder_flush(recorder->encoder, NULL))
if (encoder_end(recorder->encoder, NULL))
recorder_output_encoder_to_file(recorder, NULL);
/* now really close everything */

@@ -358,7 +358,7 @@ static void close_shout_conn(struct shout_data * sd)
sd->buf.len = 0;
if (sd->encoder != NULL) {
if (encoder_flush(sd->encoder, NULL))
if (encoder_end(sd->encoder, NULL))
write_page(sd, NULL);
encoder_close(sd->encoder);

@@ -200,11 +200,7 @@ winmm_set_buffer(struct winmm_output *wo, struct winmm_buffer *buffer,
GError **error_r)
{
void *dest = pcm_buffer_get(&buffer->buffer, size);
if (dest == NULL) {
g_set_error(error_r, winmm_output_quark(), 0,
"Out of memory");
return false;
}
assert(dest != NULL);
memcpy(dest, data, size);

@@ -27,6 +27,11 @@
#include <assert.h>
#include <string.h>
#ifdef G_OS_WIN32
#include <windows.h> // for GetACP()
#include <stdio.h> // for sprintf()
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "path"
@@ -85,11 +90,22 @@ void path_global_init(void)
charset = config_get_string(CONF_FS_CHARSET, NULL);
if (charset == NULL) {
#ifndef G_OS_WIN32
const gchar **encodings;
g_get_filename_charsets(&encodings);
if (encodings[0] != NULL && *encodings[0] != '\0')
charset = encodings[0];
#else /* G_OS_WIN32 */
/* Glib claims that file system encoding is always utf-8
* on native Win32 (i.e. not Cygwin).
* However this is true only if <gstdio.h> helpers are used.
* MPD uses regular <stdio.h> functions.
* Those functions use encoding determined by GetACP(). */
char win_charset[13];
sprintf(win_charset, "cp%u", GetACP());
charset = win_charset;
#endif
}
if (charset) {

53
src/pcm_buffer.c Normal file

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2003-2010 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 "pcm_buffer.h"
/**
* Align the specified size to the next 8k boundary.
*/
G_GNUC_CONST
static size_t
align_8k(size_t size)
{
return ((size - 1) | 0x1fff) + 1;
}
void *
pcm_buffer_get(struct pcm_buffer *buffer, size_t size)
{
assert(buffer != NULL);
if (size == 0)
/* never return NULL, because NULL would be assumed to
be an error condition */
size = 1;
if (buffer->size < size) {
/* free the old buffer */
g_free(buffer->buffer);
buffer->size = align_8k(size);
buffer->buffer = g_malloc(buffer->size);
}
assert(buffer->size >= size);
return buffer->buffer;
}

@@ -22,6 +22,8 @@
#include <glib.h>
#include <assert.h>
/**
* Manager for a temporary buffer which grows as needed. We could
* allocate a new buffer every time pcm_convert() is called, but that
@@ -39,6 +41,8 @@ struct pcm_buffer {
static inline void
pcm_buffer_init(struct pcm_buffer *buffer)
{
assert(buffer != NULL);
buffer->buffer = NULL;
buffer->size = 0;
}
@@ -49,6 +53,8 @@ pcm_buffer_init(struct pcm_buffer *buffer)
static inline void
pcm_buffer_deinit(struct pcm_buffer *buffer)
{
assert(buffer != NULL);
g_free(buffer->buffer);
buffer->buffer = NULL;
@@ -57,20 +63,13 @@ pcm_buffer_deinit(struct pcm_buffer *buffer)
/**
* Get the buffer, and guarantee a minimum size. This buffer becomes
* invalid with the next pcm_buffer_get() call.
*
* This function will never return NULL, even if size is zero, because
* the PCM library uses the NULL return value to signal "error". An
* empty destination buffer is not always an error.
*/
static inline void *
pcm_buffer_get(struct pcm_buffer *buffer, size_t size)
{
if (buffer->size < size) {
/* free the old buffer */
g_free(buffer->buffer);
/* allocate a new buffer; align at 8 kB boundaries */
buffer->size = ((size - 1) | 0x1fff) + 1;
buffer->buffer = g_malloc(buffer->size);
}
return buffer->buffer;
}
G_GNUC_MALLOC
void *
pcm_buffer_get(struct pcm_buffer *buffer, size_t size);
#endif

@@ -356,7 +356,7 @@ playlist_move_range(struct playlist *playlist,
playlist->current)
: -1;
if (to < 0 && playlist->current >= 0) {
if (start <= (unsigned)currentSong && (unsigned)currentSong <= end)
if (start <= (unsigned)currentSong && (unsigned)currentSong < end)
/* no-op, can't be moved to offset of itself */
return PLAYLIST_RESULT_SUCCESS;
to = (currentSong + abs(to)) % queue_length(&playlist->queue);

@@ -34,7 +34,6 @@
#include <assert.h>
#ifdef WIN32
#define WINVER 0x0501
#include <ws2tcpip.h>
#include <winsock.h>
#else

@@ -28,7 +28,6 @@
#include <sys/socket.h>
#include <netdb.h>
#else /* G_OS_WIN32 */
#define WINVER 0x0501
#include <ws2tcpip.h>
#include <winsock.h>
#endif /* G_OS_WIN32 */

@@ -116,6 +116,6 @@ int stats_print(struct client *client)
(long)g_timer_elapsed(stats.timer, NULL),
(long)(pc_get_total_play_time() + 0.5),
stats.song_duration,
db_get_mtime());
(long)db_get_mtime());
return 0;
}

@@ -24,6 +24,7 @@
#include <glib.h>
#include <assert.h>
#include <string.h>
struct text_input_stream {
@@ -67,7 +68,12 @@ text_input_stream_read(struct text_input_stream *tis)
do {
dest = fifo_buffer_write(tis->buffer, &length);
if (dest != NULL) {
if (dest != NULL && length >= 2) {
/* reserve one byte for the null terminator if
the last line is not terminated by a
newline character */
--length;
nbytes = input_stream_read(tis->is, dest, length,
&error);
if (nbytes > 0)
@@ -77,13 +83,22 @@ text_input_stream_read(struct text_input_stream *tis)
g_error_free(error);
return NULL;
}
}
} else
nbytes = 0;
src = fifo_buffer_read(tis->buffer, &length);
if (src == NULL)
return NULL;
p = memchr(src, '\n', length);
if (p == NULL && nbytes == 0) {
/* end of file (or line too long): terminate
the current line */
dest = fifo_buffer_write(tis->buffer, &nbytes);
assert(dest != NULL);
*(char *)dest = '\n';
fifo_buffer_append(tis->buffer, 1);
}
} while (p == NULL);
length = p - src + 1;

@@ -81,7 +81,7 @@ timer_delay(const Timer *timer)
if (delay > G_MAXINT)
delay = G_MAXINT;
return delay / 1000;
return delay;
}
void timer_sync(Timer *timer)

@@ -49,7 +49,7 @@ song_remove_event(void)
assert(removed_song != NULL);
uri = song_get_uri(removed_song);
g_debug("removing: %s", uri);
g_message("removing %s", uri);
g_free(uri);
#ifdef ENABLE_SQLITE

@@ -86,7 +86,7 @@ directory_set_stat(struct directory *dir, const struct stat *st)
{
dir->inode = st->st_ino;
dir->device = st->st_dev;
dir->stat = 1;
dir->have_stat = true;
}
static void
@@ -346,7 +346,7 @@ inodeFoundInParent(struct directory *parent, ino_t inode, dev_t device)
{
#ifndef G_OS_WIN32
while (parent) {
if (!parent->stat && statDirectory(parent) < 0)
if (!parent->have_stat && statDirectory(parent) < 0)
return -1;
if (parent->inode == inode && parent->device == device) {
g_debug("recursive directory found");
@@ -616,6 +616,8 @@ update_regular_file(struct directory *directory,
}
if (song == NULL) {
g_debug("reading %s/%s",
directory_get_path(directory), name);
song = song_file_load(name, directory);
if (song == NULL) {
g_debug("ignoring unrecognized file %s/%s",
@@ -846,6 +848,9 @@ directory_make_child_checked(struct directory *parent, const char *path)
return NULL;
}
if (skip_symlink(parent, path))
return NULL;
/* if we're adding directory paths, make sure to delete filenames
with potentially the same name */
conflicting = songvec_find(&parent->songs, base);
@@ -894,7 +899,8 @@ updatePath(const char *path)
name = g_path_get_basename(path);
if (stat_directory_child(parent, name, &st) == 0)
if (!skip_symlink(parent, name) &&
stat_directory_child(parent, name, &st) == 0)
updateInDirectory(parent, name, &st);
else
delete_name_in(parent, name);

@@ -34,13 +34,13 @@ bool uri_has_scheme(const char *uri)
const char *
uri_get_suffix(const char *uri)
{
const char *suffix = strrchr(g_basename(uri), '.');
const char *suffix = strrchr(uri, '.');
if (suffix == NULL)
return NULL;
++suffix;
if (strchr(suffix, '/') != NULL)
if (strpbrk(suffix, "/\\") != NULL)
return NULL;
return suffix;

@@ -33,7 +33,11 @@
#include <pwd.h>
#endif
#ifdef HAVE_IPV6
#if HAVE_IPV6 && WIN32
#include <winsock2.h>
#endif
#if HAVE_IPV6 && ! WIN32
#include <sys/socket.h>
#endif

@@ -24,6 +24,7 @@
#include "input_stream.h"
#include "audio_format.h"
#include "pcm_volume.h"
#include "tag_pool.h"
#include "tag_ape.h"
#include "tag_id3.h"
#include "idle.h"
@@ -103,7 +104,7 @@ decoder_data(G_GNUC_UNUSED struct decoder *decoder,
const void *data, size_t datalen,
G_GNUC_UNUSED uint16_t bit_rate)
{
write(1, data, datalen);
G_GNUC_UNUSED ssize_t nbytes = write(1, data, datalen);
return DECODE_COMMAND_NONE;
}
@@ -164,6 +165,9 @@ int main(int argc, char **argv)
decoder_name = argv[1];
path = argv[2];
g_thread_init(NULL);
tag_pool_init();
if (!input_stream_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
@@ -215,5 +219,7 @@ int main(int argc, char **argv)
}
}
tag_pool_deinit();
return 0;
}

@@ -115,7 +115,7 @@ int main(int argc, char **argv)
return 2;
}
write(1, output, length);
G_GNUC_UNUSED ssize_t ignored = write(1, output, length);
}
pcm_convert_deinit(&state);

@@ -20,6 +20,7 @@
#include "config.h"
#include "decoder_list.h"
#include "decoder_api.h"
#include "tag_pool.h"
#include "input_init.h"
#include "input_stream.h"
#include "audio_format.h"
@@ -125,7 +126,7 @@ decoder_data(G_GNUC_UNUSED struct decoder *decoder,
const void *data, size_t datalen,
G_GNUC_UNUSED uint16_t kbit_rate)
{
write(1, data, datalen);
G_GNUC_UNUSED ssize_t nbytes = write(1, data, datalen);
return DECODE_COMMAND_NONE;
}
@@ -178,8 +179,11 @@ int main(int argc, char **argv)
decoder_name = argv[1];
decoder.uri = argv[2];
g_thread_init(NULL);
g_log_set_default_handler(my_log_func, NULL);
tag_pool_init();
if (!input_stream_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
@@ -228,5 +232,7 @@ int main(int argc, char **argv)
return 1;
}
tag_pool_deinit();
return 0;
}

@@ -36,8 +36,9 @@ encoder_to_stdout(struct encoder *encoder)
size_t length;
static char buffer[32768];
while ((length = encoder_read(encoder, buffer, sizeof(buffer))) > 0)
write(1, buffer, length);
while ((length = encoder_read(encoder, buffer, sizeof(buffer))) > 0) {
G_GNUC_UNUSED ssize_t ignored = write(1, buffer, length);
}
}
int main(int argc, char **argv)
@@ -120,7 +121,7 @@ int main(int argc, char **argv)
encoder_to_stdout(encoder);
}
ret = encoder_flush(encoder, &error);
ret = encoder_end(encoder, &error);
if (!ret) {
g_printerr("encoder_flush() failed: %s\n",
error->message);

@@ -65,7 +65,8 @@ int main(int argc, char **argv)
while ((nbytes = read(0, buffer, sizeof(buffer))) > 0) {
Compressor_Process_int16(compressor,
(int16_t *)buffer, nbytes / 2);
write(1, buffer, nbytes);
G_GNUC_UNUSED ssize_t ignored = write(1, buffer, nbytes);
}
Compressor_delete(compressor);

@@ -65,6 +65,6 @@ int main(int argc, char **argv)
return 2;
}
write(1, buffer, nbytes);
G_GNUC_UNUSED ssize_t ignored = write(1, buffer, nbytes);
}
}

109
test/test_vorbis_encoder.c Normal file

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2003-2012 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 "encoder_list.h"
#include "encoder_plugin.h"
#include "audio_format.h"
#include "conf.h"
#include "stdbin.h"
#include "tag.h"
#include <glib.h>
#include <stddef.h>
#include <unistd.h>
static uint8_t zero[256];
static void
encoder_to_stdout(struct encoder *encoder)
{
size_t length;
static char buffer[32768];
while ((length = encoder_read(encoder, buffer, sizeof(buffer))) > 0) {
G_GNUC_UNUSED ssize_t ignored = write(1, buffer, length);
}
}
int
main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
{
G_GNUC_UNUSED bool success;
/* create the encoder */
const struct encoder_plugin *plugin = encoder_plugin_get("vorbis");
assert(plugin != NULL);
struct config_param *param = config_new_param(NULL, -1);
config_add_block_param(param, "quality", "5.0", -1, NULL);
struct encoder *encoder = encoder_init(plugin, param, NULL);
assert(encoder != NULL);
/* open the encoder */
struct audio_format audio_format;
audio_format_init(&audio_format, 44100, SAMPLE_FORMAT_S16, 2);
success = encoder_open(encoder, &audio_format, NULL);
assert(success);
/* write a block of data */
success = encoder_write(encoder, zero, sizeof(zero), NULL);
assert(success);
encoder_to_stdout(encoder);
/* write a tag */
success = encoder_pre_tag(encoder, NULL);
assert(success);
encoder_to_stdout(encoder);
struct tag *tag = tag_new();
tag_add_item(tag, TAG_ARTIST, "Foo");
tag_add_item(tag, TAG_TITLE, "Bar");
success = encoder_tag(encoder, tag, NULL);
assert(success);
tag_free(tag);
encoder_to_stdout(encoder);
/* write another block of data */
success = encoder_write(encoder, zero, sizeof(zero), NULL);
assert(success);
/* finish */
success = encoder_end(encoder, NULL);
assert(success);
encoder_to_stdout(encoder);
encoder_close(encoder);
encoder_finish(encoder);
}