Compare commits

..

125 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
Avuton Olrich
3041409334 mpd version 0.16.5 2011-10-09 04:44:51 -07:00
Max Kellermann
71536eb412 decoder/wavpack: don't call WavpackGetMode() twice
Use local variable "is_float".
2011-10-08 15:37:47 +02:00
Max Kellermann
fe77230d84 pcm_convert: fix typo in error message 2011-10-08 15:36:55 +02:00
Max Kellermann
5ed0eb51d1 output/openal: auto-fallback to mono if channel count is unsupported
.. instead of failing playback completely.
2011-10-08 14:41:22 +02:00
Max Kellermann
72a1ca3b99 output/alsa: remove "default" case from switch
Allow gcc to warn when a new format isn't supported.
2011-10-08 14:41:11 +02:00
Max Kellermann
72ff9bd3e6 configure.ac: disable systemd service by default
Defaulting to "with systemd" causes problems for users who install MPD
as an unprivileged user, and it breaks "make distcheck".  It looks
like enabling it by default creates too many practical problems for
unexperienced users.

With --with-systemdsystemunitdir (without a parameter), configure.ac
attempts to auto-detect systemd.
2011-10-07 09:56:38 +02:00
Jesús Bravo Álvarez
039b354490 playlist_song: fix absolute path support in playlists
Right now, a playlist with absolute pathnames can only add songs that
are in the same the directory of the playlist or under it.

If uri is an absolute pathname and base_uri is set,
playlist_check_translate_song() will check that base_uri is a prefix
of uri, excluding every other song in the music directory outside
base_uri.

I think in this case base_uri should be completely ignored (and made
NULL) and uri should just be checked against music root directory.
2011-10-06 22:21:24 +02:00
Max Kellermann
b2f03e76ff player_thread: add flag "output_open", fixes assertion failure
Previously, the condition "defined(play_audio_format)" was used to see
if an output device has been opened, but if the device had failed on
startup, an assertion failure could occur.  This patch adds a separate
flag.
2011-10-06 21:22:48 +02:00
Max Kellermann
63b33b6ec5 player_thread: move code to player_open_output()
Common function that manages "player" attributes after
audio_output_all_open() has returned.
2011-10-06 20:55:52 +02:00
Max Kellermann
23670795db output_control: remove unused prototype _close_locked() 2011-10-06 19:51:37 +02:00
Max Kellermann
8ea6c113b5 player_control: auto-start playback when seeking is requested
Now that the player thread can handle SEEK commands while not (yet)
playing, we can remove the "pc.state" check from pc_seek().
2011-10-06 00:35:54 +02:00
Max Kellermann
37f026a0a6 player_thread: handle SEEK while not playing 2011-10-06 00:35:53 +02:00
Max Kellermann
f67136df19 decoder_api: call _prepare_initial_seek() in decoder_tag()
This checks both conditions: pending and running.  Fixes yet another
assertion failure!
2011-10-06 00:35:53 +02:00
Max Kellermann
e07073ff28 decoder_api: move code to _prepare_initial_seek()
.. and add a few code comments.
2011-10-06 00:35:53 +02:00
Max Kellermann
64b0ba6da7 decoder_control: add attributes start_ms, end_ms
Don't read song.start_ms and song.end_ms, let the player thread manage
this logic instead.
2011-10-05 23:15:22 +02:00
Max Kellermann
99d4ae0c1a decoder_api: don't copy tag to pipe during initial seek
Fixes one more assertion failure.
2011-10-05 22:54:30 +02:00
Max Kellermann
f185b35088 decoder_api: clear initial_seek_running on error
Fixes possible assertion failure.
2011-10-04 22:29:31 +02:00
Miklos Vajna
83f6498aac Install systemd service file if systemd is available 2011-09-30 08:37:36 +02:00
Max Kellermann
525a791987 decoder_api: emulate SEEK command for initial seek to CUE track
When playing a CUE track, the player thread waited for the decoder to
become ready, and then sent a SEEK command to the beginning of the CUE
track.  If that is near the start of the song file, and the track is
short enough, the decoder could have finished decoding already at that
point, and seeking fails.

This commit makes this initial seek more robust: instead of letting
the player thread deal with the difficult timings, let the decoder API
emulate a SEEK command, and return it to the decoder plugin, as soon
as the plugin finishes its initialization.
2011-09-22 00:04:48 +02:00
Max Kellermann
fb19210cfd decoder_internal.h: more API documentation 2011-09-22 00:04:20 +02:00
Jonathan Neuschäfer
b0b2c5b3e0 utils: uri.h: fix a typo: "schema" 2011-09-21 17:47:28 +02:00
Max Kellermann
29742d23d3 configure.ac: fix --enable-id3 help string 2011-09-20 22:18:42 +02:00
Max Kellermann
c476819cb1 fd_util: add function close_socket()
Wrap close(), use closesocket() on WIN32/WinSock.
2011-09-20 08:38:58 +02:00
Max Kellermann
77a56c7c5a fd_util: check HAVE_INOTIFY_INIT in header
Don't provide the prototype if inotify_init() was not detected.
2011-09-20 08:38:17 +02:00
Max Kellermann
46deb7ca82 fd_util: include check.h, verify config.h was included 2011-09-20 08:38:08 +02:00
Max Kellermann
b03f9ece05 glib_socket.h: wrap g_io_channel_*_new() calls portably
The server_socket library (used by the httpd output plugin) didn't
check for WIN32, that's fixed now.
2011-09-20 08:35:25 +02:00
Max Kellermann
1d8840412f configure.ac: add option --enable-solaris-output
Allow enabling the plugin explicitly without running Solaris, to test
the build.
2011-09-19 09:39:35 +02:00
Max Kellermann
df1152ee0f configure.ac: fix solaris result display
Wrong variable name.
2011-09-19 09:39:05 +02:00
Tim Harder
79435dbdec decoder/audiofile: include stdio header for SEEK_* defines
The stdio header is no longer pulled in by af_vfs.h in audiofile-0.3.0.
2011-09-17 07:33:42 +02:00
Max Kellermann
27206368da output/pulse: improve locking
Always lock the main loop when operating on PULSE objects.  Document
this.
2011-09-17 07:30:59 +02:00
Max Kellermann
443e96381a configure.ac: disable assertions in the non-debugging build
Add -DNDEBUG to AM_CPPFLAGS.
2011-09-16 07:41:41 +02:00
Max Kellermann
1cbba4fc59 input/curl, output/pulse: fix "unused local variable" warnings 2011-09-16 07:41:41 +02:00
Max Kellermann
344b6dd179 configure.ac: enable -Werror for C++ 2011-09-16 07:38:00 +02:00
Max Kellermann
d8c829fa0c configure.ac: pass AM_CXXFLAGS, AM_CPPFLAGS to Makefile.am 2011-09-16 07:35:46 +02:00
Max Kellermann
2ed870c854 decoder/ffmpeg: flush the codec after seeking
Let the codec start with fresh buffers.  This should fix the remaining
seeking issues.
2011-09-15 21:41:25 +02:00
Max Kellermann
ce35ba9ac9 decoder/ffmpeg: explicitly specify the current stream for seeking
Use AVStream.time_base to convert the decoder_seek_where() value, and
pass the current stream number to av_seek_frame().
2011-09-15 21:35:29 +02:00
Max Kellermann
724a59aaf7 decoder/ffmpeg: don't require key frame for seeking
Use flag AV_TIME_BASE.
2011-09-15 21:32:29 +02:00
Max Kellermann
42d8c2981f decoder/ffmpeg: higher precision timestamps 2011-09-15 21:30:27 +02:00
Max Kellermann
9aa91e0f17 decoder/ffmpeg: move formula to time_from_ffmpeg() 2011-09-15 21:23:48 +02:00
Max Kellermann
5aabee8996 decoder/ffmpeg: add local variable "av_stream"
Code simplification.
2011-09-15 21:14:53 +02:00
Max Kellermann
48a84ca23e input/rewind: copy the MIME type only once
Reduce heap usage by reducing the number of malloc() / free() calls.
2011-09-15 20:24:15 +02:00
Max Kellermann
c345c5ebae .gitignore: add doxygen.conf 2011-09-09 23:21:20 +02:00
Max Kellermann
5cf4ce9318 pcm_format: fix 32-to-24 bit conversion (the "silence" bug)
D'oh, we were reading 16 bit integers instead of 32 bit integers!
That caused silence when trying to play a 32 bit input file on a 24
bit sound card (e.g. USB sound chips with 24 bit packed samples).
2011-09-08 23:47:32 +02:00
Avuton Olrich
5469941f2b Modify version string to post-release version 0.16.5~git 2011-09-01 17:58:29 -07:00
76 changed files with 1537 additions and 365 deletions

2
.gitignore vendored
View File

@@ -34,11 +34,13 @@ missing
mkinstalldirs
mpd
mpd.exe
mpd.service
stamp-h1
tags
*~
.#*
.stgit*
doc/doxygen.conf
doc/protocol.html
doc/protocol
doc/user

View File

@@ -1,7 +1,7 @@
ACLOCAL_AMFLAGS = -I m4
AUTOMAKE_OPTIONS = foreign 1.10 dist-bzip2 subdir-objects
AM_CPPFLAGS = -I$(srcdir)/src $(GLIB_CFLAGS)
AM_CPPFLAGS += -I$(srcdir)/src $(GLIB_CFLAGS)
AM_CPPFLAGS += -DSYSTEM_CONFIG_FILE_LOCATION='"$(sysconfdir)/mpd.conf"'
@@ -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 \
@@ -243,6 +241,7 @@ src_mpd_SOURCES = \
$(OUTPUT_API_SRC) $(OUTPUT_SRC) \
$(MIXER_API_SRC) $(MIXER_SRC) \
$(FILTER_SRC) \
src/glib_socket.h \
src/notify.c \
src/audio.c \
src/audio_check.c \
@@ -266,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 \
@@ -299,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 \
@@ -801,6 +802,15 @@ FILTER_SRC = \
src/filter/volume_filter_plugin.c
#
# systemd unit
#
if HAVE_SYSTEMD
systemdsystemunit_DATA = \
mpd.service
endif
#
# Sparse code analysis
#
@@ -918,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) \
@@ -940,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) \
@@ -958,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 \
@@ -980,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)
@@ -988,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 \
@@ -1009,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 \
@@ -1042,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 \
@@ -1059,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 \
@@ -1077,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

69
NEWS
View File

@@ -1,3 +1,72 @@
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
- add option --enable-solaris-output
* pcm_format: fix 32-to-24 bit conversion (the "silence" bug)
* input:
- rewind: reduce heap usage
* decoder:
- ffmpeg: higher precision timestamps
- ffmpeg: don't require key frame for seeking
- fix CUE track seeking
* output:
- openal: auto-fallback to mono if channel count is unsupported
* player:
- make seeking to CUE track more reliable
- the "seek" command works when MPD is stopped
- restore song position from state file (bug fix)
- fix crash that sometimes occurred when audio device fails on startup
- fix absolute path support in playlists
* WIN32: close sockets properly
* install systemd service file if systemd is available
ver 0.16.4 (2011/09/01)
* don't abort configure when avahi is not found
* auto-detect libmad without pkg-config

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT(mpd, 0.16.4, 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)
@@ -33,11 +33,28 @@ fi
AC_PROG_INSTALL
AC_PROG_MAKE_SET
PKG_PROG_PKG_CONFIG
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
[], [with_systemdsystemunitdir=no])
if test "x$with_systemdsystemunitdir" = xyes; then
AC_MSG_CHECKING(for systemd)
with_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd)
if test -z "$with_systemdsystemunitdir"; then
AC_MSG_ERROR([Failed to detect systemd])
fi
AC_MSG_RESULT([$with_systemdsystemunitdir])
fi
if test "x$with_systemdsystemunitdir" != xno; then
AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])
fi
AM_CONDITIONAL(HAVE_SYSTEMD, [test -n "$with_systemdsystemunitdir" -a "x$with_systemdsystemunitdir" != xno ])
dnl ---------------------------------------------------------------------------
dnl Declare Variables
dnl ---------------------------------------------------------------------------
AC_SUBST(AM_CPPFLAGS,"")
AC_SUBST(AM_CFLAGS,"")
AC_SUBST(AM_CXXFLAGS,"")
AC_SUBST(MPD_LIBS)
AC_SUBST(MPD_CFLAGS)
@@ -51,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
@@ -197,7 +215,7 @@ AC_ARG_ENABLE(httpd-output,
AC_ARG_ENABLE(id3,
AS_HELP_STRING([--enable-id3],
[disable id3 support]),,
[enable id3 support]),,
enable_id3=auto)
AC_ARG_ENABLE(inotify,
@@ -322,6 +340,11 @@ AC_ARG_ENABLE(sndfile,
[enable sndfile support]),,
enable_sndfile=auto)
AC_ARG_ENABLE(solaris_output,
AS_HELP_STRING([--enable-solaris-output],
[enables the Solaris /dev/audio output]),,
[enable_solaris_output=auto])
AC_ARG_ENABLE(sqlite,
AS_HELP_STRING([--enable-sqlite],
[enable support for the SQLite database]),,
@@ -409,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 ---------------------------------------------------------------------------
@@ -423,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
@@ -1368,16 +1400,22 @@ fi
AM_CONDITIONAL(HAVE_SHOUT, test x$enable_shout = xyes)
dnl --------------------------------- Solaris ---------------------------------
case "$host_os" in
if test x$enable_solaris_output = xauto; then
case "$host_os" in
solaris*)
AC_DEFINE(ENABLE_SOLARIS_OUTPUT, 1, [Define to enable Solaris /dev/audio support])
enable_solaris_output=yes
;;
*)
enable_solaris_output=no
;;
esac
esac
fi
if test x$enable_solaris_output = xyes; then
AC_DEFINE(ENABLE_SOLARIS_OUTPUT, 1, [Define to enable Solaris /dev/audio support])
fi
AM_CONDITIONAL(ENABLE_SOLARIS_OUTPUT, test x$enable_solaris_output = xyes)
@@ -1449,17 +1487,15 @@ dnl CFLAGS
dnl ---------------------------------------------------------------------------
dnl ---------------------------------- debug ----------------------------------
#if test "x$enable_debug" = xno; then
# don't set NDEBUG for now, until MPD is stable
#AM_CFLAGS="$AM_CFLAGS -DNDEBUG"
#fi
if test "x$enable_debug" = xno; then
AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"
fi
dnl ----------------------------------- GCC -----------------------------------
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])
@@ -1478,6 +1514,7 @@ fi
dnl ---------------------------- warnings as errors ---------------------------
if test "x$enable_werror" = xyes; then
AM_CFLAGS="$AM_CFLAGS -Werror -pedantic-errors"
AM_CXXFLAGS="$AM_CXXFLAGS -Werror"
fi
dnl ---------------------------------------------------------------------------
@@ -1551,7 +1588,7 @@ results(pulse, [PulseAudio])
results(mvp, [Media MVP])
results(shout, [SHOUTcast])
printf '\n\t'
results(solaris, [Solaris])
results(solaris_output, [Solaris])
results(winmm_output, [WinMM])
if
@@ -1579,5 +1616,6 @@ dnl ---------------------------------------------------------------------------
dnl Generate files
dnl ---------------------------------------------------------------------------
AC_OUTPUT(Makefile)
AC_OUTPUT(mpd.service)
echo 'MPD is ready for compilation, type "make" to begin.'

9
mpd.service.in Normal file
View File

@@ -0,0 +1,9 @@
[Unit]
Description=Music Player Daemon
After=sound.target
[Service]
ExecStart=@prefix@/bin/mpd --no-daemon
[Install]
WantedBy=multi-user.target

View File

@@ -19,9 +19,11 @@
#include "config.h"
#include "client_internal.h"
#include "fd_util.h"
#include "fifo_buffer.h"
#include "socket_util.h"
#include "permission.h"
#include "glib_socket.h"
#include <assert.h>
#include <sys/types.h>
@@ -66,7 +68,7 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
progname, hostaddr);
g_free(hostaddr);
close(fd);
close_socket(fd);
return;
}
@@ -76,17 +78,13 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
if (client_list_is_full()) {
g_warning("Max Connections Reached!");
close(fd);
close_socket(fd);
return;
}
client = g_new0(struct client, 1);
#ifndef G_OS_WIN32
client->channel = g_io_channel_unix_new(fd);
#else
client->channel = g_io_channel_win32_new_socket(fd);
#endif
client->channel = g_io_channel_new_socket(fd);
/* GLib is responsible for closing the file descriptor */
g_io_channel_set_close_on_unref(client->channel, true);
/* NULL encoding means the stream is binary safe; the MPD

View File

@@ -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;;

View File

@@ -25,6 +25,7 @@
#include <af_vfs.h>
#include <assert.h>
#include <glib.h>
#include <stdio.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "audiofile"
@@ -63,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;
@@ -85,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);

View File

@@ -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,59 @@ align16(void *p, size_t *length_p)
*length_p -= add;
return (char *)p + add;
}
#endif
G_GNUC_CONST
static double
time_from_ffmpeg(int64_t t, const AVRational time_base)
{
assert(t != (int64_t)AV_NOPTS_VALUE);
return (double)av_rescale_q(t, time_base, (AVRational){1, 1024})
/ (double)1024;
}
G_GNUC_CONST
static int64_t
time_to_ffmpeg(double t, const AVRational time_base)
{
return av_rescale_q((int64_t)(t * 1024), (AVRational){1, 1024},
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,
@@ -222,8 +289,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
{
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
decoder_timestamp(decoder,
av_rescale_q(packet->pts, *time_base,
(AVRational){1, 1}));
time_from_ffmpeg(packet->pts, *time_base));
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
AVPacket packet2 = *packet;
@@ -232,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 (
@@ -245,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);
@@ -284,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:
@@ -360,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;
}
@@ -370,13 +475,18 @@ 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;
}
AVCodecContext *codec_context =
format_context->streams[audio_stream]->codec;
AVStream *av_stream = format_context->streams[audio_stream];
AVCodecContext *codec_context = av_stream->codec;
if (codec_context->codec_name[0] != 0)
g_debug("codec '%s'", codec_context->codec_name);
@@ -384,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;
}
@@ -404,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;
}
@@ -427,7 +558,7 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
if (packet.stream_index == audio_stream)
cmd = ffmpeg_send_packet(decoder, input,
&packet, codec_context,
&format_context->streams[audio_stream]->time_base);
&av_stream->time_base);
else
cmd = decoder_get_command(decoder);
@@ -435,17 +566,25 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
if (cmd == DECODE_COMMAND_SEEK) {
int64_t where =
decoder_seek_where(decoder) * AV_TIME_BASE;
time_to_ffmpeg(decoder_seek_where(decoder),
av_stream->time_base);
if (av_seek_frame(format_context, -1, where, 0) < 0)
if (av_seek_frame(format_context, audio_stream, where,
AV_TIME_BASE) < 0)
decoder_seek_error(decoder);
else
else {
avcodec_flush_buffers(codec_context);
decoder_command_finished(decoder);
}
}
} 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);
}
@@ -456,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
@@ -521,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;
}
@@ -534,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);
@@ -568,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;

View File

@@ -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);
}

View File

@@ -176,7 +176,7 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
return;
}
if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT) {
if (is_float) {
format_samples = format_samples_float;
} else {
format_samples = format_samples_int;

View File

@@ -78,15 +78,76 @@ decoder_initialized(struct decoder *decoder,
&af_string));
}
enum decoder_command decoder_get_command(G_GNUC_UNUSED struct decoder * decoder)
/**
* Checks if we need an "initial seek". If so, then the initial seek
* is prepared, and the function returns true.
*/
G_GNUC_PURE
static bool
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 */
decoder->initial_seek_pending = false;
decoder->initial_seek_running = true;
return true;
}
/* skip initial seek when there's another command
(e.g. STOP) */
decoder->initial_seek_pending = false;
}
return false;
}
/**
* Returns the current decoder command. May return a "virtual"
* synthesized command, e.g. to seek to the beginning of the CUE
* track.
*/
G_GNUC_PURE
static enum decoder_command
decoder_get_virtual_command(struct decoder *decoder)
{
const struct decoder_control *dc = decoder->dc;
assert(dc->pipe != NULL);
if (decoder_prepare_initial_seek(decoder))
return DECODE_COMMAND_SEEK;
return dc->command;
}
enum decoder_command
decoder_get_command(struct decoder *decoder)
{
return decoder_get_virtual_command(decoder);
}
void
decoder_command_finished(struct decoder *decoder)
{
@@ -94,11 +155,24 @@ decoder_command_finished(struct decoder *decoder)
decoder_lock(dc);
assert(dc->command != DECODE_COMMAND_NONE);
assert(dc->command != DECODE_COMMAND_NONE ||
decoder->initial_seek_running);
assert(dc->command != DECODE_COMMAND_SEEK ||
decoder->initial_seek_running ||
dc->seek_error || decoder->seeking);
assert(dc->pipe != NULL);
if (decoder->initial_seek_running) {
assert(!decoder->seeking);
assert(decoder->chunk == NULL);
assert(music_pipe_empty(dc->pipe));
decoder->initial_seek_running = false;
decoder->timestamp = dc->start_ms / 1000.;
decoder_unlock(dc);
return;
}
if (decoder->seeking) {
decoder->seeking = false;
@@ -124,9 +198,13 @@ double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
{
const struct decoder_control *dc = decoder->dc;
assert(dc->command == DECODE_COMMAND_SEEK);
assert(dc->pipe != NULL);
if (decoder->initial_seek_running)
return dc->start_ms / 1000.;
assert(dc->command == DECODE_COMMAND_SEEK);
decoder->seeking = true;
return dc->seek_where;
@@ -136,9 +214,17 @@ void decoder_seek_error(struct decoder * decoder)
{
struct decoder_control *dc = decoder->dc;
assert(dc->command == DECODE_COMMAND_SEEK);
assert(dc->pipe != NULL);
if (decoder->initial_seek_running) {
/* d'oh, we can't seek to the sub-song start position,
what now? - no idea, ignoring the problem for now. */
decoder->initial_seek_running = false;
return;
}
assert(dc->command == DECODE_COMMAND_SEEK);
dc->seek_error = true;
decoder->seeking = false;
@@ -270,7 +356,7 @@ decoder_data(struct decoder *decoder,
assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
decoder_lock(dc);
cmd = dc->command;
cmd = decoder_get_virtual_command(decoder);
decoder_unlock(dc);
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
@@ -357,8 +443,8 @@ decoder_data(struct decoder *decoder,
decoder->timestamp += (double)nbytes /
audio_format_time_to_size(&dc->out_audio_format);
if (dc->song->end_ms > 0 &&
decoder->timestamp >= dc->song->end_ms / 1000.0)
if (dc->end_ms > 0 &&
decoder->timestamp >= dc->end_ms / 1000.0)
/* the end of this range has been reached:
stop decoding */
return DECODE_COMMAND_STOP;
@@ -388,6 +474,14 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
update_stream_tag(decoder, is);
/* check if we're seeking */
if (decoder_prepare_initial_seek(decoder))
/* during initial seek, no music chunk must be created
until seeking is finished; skip the rest of the
function here */
return DECODE_COMMAND_SEEK;
/* send tag to music pipe */
if (decoder->stream_tag != NULL) {

View File

@@ -102,6 +102,7 @@ dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
void
dc_start(struct decoder_control *dc, struct song *song,
unsigned start_ms, unsigned end_ms,
struct music_buffer *buffer, struct music_pipe *pipe)
{
assert(song != NULL);
@@ -110,6 +111,8 @@ dc_start(struct decoder_control *dc, struct song *song,
assert(music_pipe_empty(pipe));
dc->song = song;
dc->start_ms = start_ms;
dc->end_ms = end_ms;
dc->buffer = buffer;
dc->pipe = pipe;
dc_command(dc, DECODE_COMMAND_START);

View File

@@ -79,6 +79,23 @@ struct decoder_control {
*/
const struct song *song;
/**
* The initial seek position (in milliseconds), e.g. to the
* start of a sub-track described by a CUE file.
*
* This attribute is set by dc_start().
*/
unsigned start_ms;
/**
* The decoder will stop when it reaches this position (in
* milliseconds). 0 means don't stop before the end of the
* file.
*
* This attribute is set by dc_start().
*/
unsigned end_ms;
float total_time;
/** the #music_chunk allocator */
@@ -225,11 +242,14 @@ dc_command_wait(struct decoder_control *dc);
*
* @param the decoder
* @param song the song to be decoded
* @param start_ms see #decoder_control
* @param end_ms see #decoder_control
* @param pipe the pipe which receives the decoded chunks (owned by
* the caller)
*/
void
dc_start(struct decoder_control *dc, struct song *song,
unsigned start_ms, unsigned end_ms,
struct music_buffer *buffer, struct music_pipe *pipe);
void

View File

@@ -36,6 +36,25 @@ struct decoder {
*/
double timestamp;
/**
* Is the initial seek (to the start position of the sub-song)
* pending, or has it been performed already?
*/
bool initial_seek_pending;
/**
* Is the initial seek currently running? During this time,
* the decoder command is SEEK. This flag is set by
* decoder_get_virtual_command(), when the virtual SEEK
* command is generated for the first time.
*/
bool initial_seek_running;
/**
* This flag is set by decoder_seek_where(), and checked by
* decoder_command_finished(). It is used to clean up after
* seeking.
*/
bool seeking;
/**

View File

@@ -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"
@@ -369,6 +370,8 @@ decoder_run_song(struct decoder_control *dc,
{
struct decoder decoder = {
.dc = dc,
.initial_seek_pending = dc->start_ms > 0,
.initial_seek_running = false,
};
int ret;

View File

@@ -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

View File

@@ -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)];
};

View File

@@ -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,

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -159,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);
}

View File

@@ -304,3 +304,13 @@ inotify_init_cloexec(void)
}
#endif
int
close_socket(int fd)
{
#ifdef WIN32
return closesocket(fd);
#else
return close(fd);
#endif
}

View File

@@ -36,6 +36,8 @@
#ifndef FD_UTIL_H
#define FD_UTIL_H
#include "check.h"
#include <stdbool.h>
#include <stddef.h>
@@ -120,6 +122,8 @@ recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags);
#endif
#ifdef HAVE_INOTIFY_INIT
/**
* Wrapper for inotify_init(), which sets the CLOEXEC flag (atomically
* if supported by the OS).
@@ -128,3 +132,11 @@ int
inotify_init_cloexec(void);
#endif
/**
* Portable wrapper for close(); use closesocket() on WIN32/WinSock.
*/
int
close_socket(int fd);
#endif

View File

@@ -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)
{

View File

@@ -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

40
src/glib_socket.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MPD_GLIB_SOCKET_H
#define MPD_GLIB_SOCKET_H
#include <glib.h>
/**
* Portable wrapper for g_io_channel_unix_new() or
* g_io_channel_win32_new_socket().
*/
G_GNUC_MALLOC
static inline GIOChannel *
g_io_channel_new_socket(int fd)
{
#ifdef G_OS_WIN32
return g_io_channel_win32_new_socket(fd);
#else
return g_io_channel_unix_new(fd);
#endif
}
#endif

90
src/growing_fifo.c Normal file
View 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
View 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

View File

@@ -177,7 +177,7 @@ buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
assert(buffer->consumed <= buffer->size);
g_free(data);
g_free(buffer);
}
/**

View File

@@ -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;

View File

@@ -83,12 +83,14 @@ copy_attributes(struct input_rewind *r)
assert(dest != src);
assert(src->mime == NULL || dest->mime != src->mime);
bool dest_ready = dest->ready;
dest->ready = src->ready;
dest->seekable = src->seekable;
dest->size = src->size;
dest->offset = src->offset;
if (src->mime != NULL) {
if (!dest_ready && src->ready) {
g_free(dest->mime);
dest->mime = g_strdup(src->mime);
}

View File

@@ -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);
}

View File

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

View File

@@ -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)

View File

@@ -186,6 +186,9 @@ static snd_pcm_format_t
get_bitformat(enum sample_format sample_format)
{
switch (sample_format) {
case SAMPLE_FORMAT_UNDEFINED:
return SND_PCM_FORMAT_UNKNOWN;
case SAMPLE_FORMAT_S8:
return SND_PCM_FORMAT_S8;
@@ -202,10 +205,10 @@ get_bitformat(enum sample_format sample_format)
case SAMPLE_FORMAT_S32:
return SND_PCM_FORMAT_S32;
default:
return SND_PCM_FORMAT_UNKNOWN;
}
assert(false);
return SND_PCM_FORMAT_UNKNOWN;
}
static snd_pcm_format_t

View File

@@ -24,6 +24,7 @@
#include "page.h"
#include "icy_server.h"
#include "glib_compat.h"
#include "glib_socket.h"
#include <stdbool.h>
#include <assert.h>
@@ -459,11 +460,7 @@ httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported)
client->httpd = httpd;
#ifndef G_OS_WIN32
client->channel = g_io_channel_unix_new(fd);
#else
client->channel = g_io_channel_win32_new_socket(fd);
#endif
client->channel = g_io_channel_new_socket(fd);
/* GLib is responsible for closing the file descriptor */
g_io_channel_set_close_on_unref(client->channel, true);

View File

@@ -201,7 +201,7 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
g_warning("libwrap refused connection (libwrap=%s) from %s",
progname, hostaddr);
g_free(hostaddr);
close(fd);
close_socket(fd);
g_mutex_unlock(httpd->mutex);
return;
}
@@ -222,7 +222,7 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
httpd->clients_cnt < httpd->clients_max))
httpd_client_add(httpd, fd);
else
close(fd);
close_socket(fd);
} else if (fd < 0 && errno != EINTR) {
g_warning("accept() failed: %s", g_strerror(errno));
}

View File

@@ -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;

View File

@@ -58,32 +58,26 @@ 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)
return AL_FORMAT_STEREO16;
if (audio_format->channels == 1)
return AL_FORMAT_MONO16;
break;
case SAMPLE_FORMAT_S8:
if (audio_format->channels == 2)
return AL_FORMAT_STEREO8;
if (audio_format->channels == 1)
return AL_FORMAT_MONO8;
break;
/* 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;
if (audio_format->channels == 2)
return AL_FORMAT_STEREO16;
if (audio_format->channels == 1)
return AL_FORMAT_MONO16;
break;
return openal_audio_format(audio_format);
}
return 0;
}
static bool

View File

@@ -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 = {

View File

@@ -72,7 +72,8 @@ pulse_output_set_mixer(struct pulse_output *po, struct pulse_mixer *pm)
}
void
pulse_output_clear_mixer(struct pulse_output *po, struct pulse_mixer *pm)
pulse_output_clear_mixer(struct pulse_output *po,
G_GNUC_UNUSED struct pulse_mixer *pm)
{
assert(po != NULL);
assert(pm != NULL);
@@ -247,6 +248,8 @@ pulse_output_delete_stream(struct pulse_output *po)
/**
* Frees and clears the context.
*
* Caller must lock the main loop.
*/
static void
pulse_output_delete_context(struct pulse_output *po)
@@ -265,6 +268,8 @@ pulse_output_delete_context(struct pulse_output *po)
/**
* Create, set up and connect a context.
*
* Caller must lock the main loop.
*
* @return true on success, false on error
*/
static bool
@@ -355,12 +360,8 @@ pulse_output_enable(void *data, GError **error_r)
return false;
}
pa_threaded_mainloop_unlock(po->mainloop);
/* create the libpulse context and connect it */
pa_threaded_mainloop_lock(po->mainloop);
if (!pulse_output_setup_context(po, error_r)) {
pa_threaded_mainloop_unlock(po->mainloop);
pa_threaded_mainloop_stop(po->mainloop);
@@ -392,6 +393,8 @@ pulse_output_disable(void *data)
* Check if the context is (already) connected, and waits if not. If
* the context has been disconnected, retry to connect.
*
* Caller must lock the main loop.
*
* @return true on success, false on error
*/
static bool
@@ -401,8 +404,6 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
pa_context_state_t state;
pa_threaded_mainloop_lock(po->mainloop);
if (po->context == NULL && !pulse_output_setup_context(po, error_r))
return false;
@@ -411,7 +412,6 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
switch (state) {
case PA_CONTEXT_READY:
/* nothing to do */
pa_threaded_mainloop_unlock(po->mainloop);
return true;
case PA_CONTEXT_UNCONNECTED:
@@ -422,7 +422,6 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
"failed to connect: %s",
pa_strerror(pa_context_errno(po->context)));
pulse_output_delete_context(po);
pa_threaded_mainloop_unlock(po->mainloop);
return false;
case PA_CONTEXT_CONNECTING:
@@ -505,6 +504,8 @@ pulse_output_open(void *data, struct audio_format *audio_format,
assert(po->mainloop != NULL);
pa_threaded_mainloop_lock(po->mainloop);
if (po->context != NULL) {
switch (pa_context_get_state(po->context)) {
case PA_CONTEXT_UNCONNECTED:
@@ -524,8 +525,10 @@ pulse_output_open(void *data, struct audio_format *audio_format,
}
}
if (!pulse_output_wait_connection(po, error_r))
if (!pulse_output_wait_connection(po, error_r)) {
pa_threaded_mainloop_unlock(po->mainloop);
return false;
}
/* MPD doesn't support the other pulseaudio sample formats, so
we just force MPD to send us everything as 16 bit */
@@ -535,8 +538,6 @@ pulse_output_open(void *data, struct audio_format *audio_format,
ss.rate = audio_format->sample_rate;
ss.channels = audio_format->channels;
pa_threaded_mainloop_lock(po->mainloop);
/* create a stream .. */
po->stream = pa_stream_new(po->context, po->name, &ss, NULL);

View File

@@ -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 */

View File

@@ -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);

View File

@@ -23,7 +23,6 @@
#include <glib.h>
#include <sys/audio.h>
#include <sys/stropts.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -31,6 +30,25 @@
#include <fcntl.h>
#include <errno.h>
#ifdef __sun
#include <sys/audio.h>
#else
/* some fake declarations that allow build this plugin on systems
other than Solaris, just to see if it compiles */
#define AUDIO_GETINFO 0
#define AUDIO_SETINFO 0
#define AUDIO_ENCODING_LINEAR 0
struct audio_info {
struct {
unsigned sample_rate, channels, precision, encoding;
} play;
};
#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "solaris_output"

View File

@@ -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);

View File

@@ -102,9 +102,6 @@ audio_output_disable(struct audio_output *ao)
g_mutex_unlock(ao->mutex);
}
static void
audio_output_close_locked(struct audio_output *ao);
/**
* Object must be locked (and unlocked) by the caller.
*/

View File

@@ -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
View 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;
}

View File

@@ -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

View File

@@ -222,7 +222,7 @@ pcm_convert_32(struct pcm_convert_state *state,
src_buffer, src_size, &len);
if (buf == NULL) {
g_set_error(error_r, pcm_convert_quark(), 0,
"Conversion from %s to 24 bit is not implemented",
"Conversion from %s to 32 bit is not implemented",
sample_format_to_string(src_format->format));
return NULL;
}

View File

@@ -143,7 +143,7 @@ pcm_convert_16_to_24(int32_t *out, const int16_t *in,
}
static void
pcm_convert_32_to_24(int32_t *out, const int16_t *in,
pcm_convert_32_to_24(int32_t *out, const int32_t *in,
unsigned num_samples)
{
while (num_samples > 0) {
@@ -197,7 +197,7 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
*dest_size_r = num_samples * sizeof(*dest);
dest = pcm_buffer_get(buffer, *dest_size_r);
pcm_convert_32_to_24(dest, (const int16_t *)src,
pcm_convert_32_to_24(dest, (const int32_t *)src,
num_samples);
return dest;
}

View File

@@ -309,9 +309,6 @@ pc_seek(struct song *song, float seek_time)
{
assert(song != NULL);
if (pc.state == PLAYER_STATE_STOP)
return false;
player_lock();
pc.next_song = song;
pc.seek_where = seek_time;

View File

@@ -73,6 +73,14 @@ struct player {
*/
bool queued;
/**
* Was any audio output opened successfully? It might have
* failed meanwhile, but was not explicitly closed by the
* player thread. When this flag is unset, some output
* methods must not be called.
*/
bool output_open;
/**
* the song currently being played
*/
@@ -145,7 +153,13 @@ player_dc_start(struct player *player, struct music_pipe *pipe)
assert(player->queued || pc.command == PLAYER_COMMAND_SEEK);
assert(pc.next_song != NULL);
dc_start(dc, pc.next_song, player_buffer, pipe);
unsigned start_ms = pc.next_song->start_ms;
if (pc.command == PLAYER_COMMAND_SEEK)
start_ms += (unsigned)(pc.seek_where * 1000);
dc_start(dc, pc.next_song,
start_ms, pc.next_song->end_ms,
player_buffer, pipe);
}
/**
@@ -270,6 +284,44 @@ real_song_duration(const struct song *song, double decoder_duration)
return decoder_duration - song->start_ms / 1000.0;
}
/**
* Wrapper for audio_output_all_open(). Upon failure, it pauses the
* player.
*
* @return true on success
*/
static bool
player_open_output(struct player *player)
{
assert(audio_format_defined(&player->play_audio_format));
assert(pc.state == PLAYER_STATE_PLAY ||
pc.state == PLAYER_STATE_PAUSE);
if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
player->output_open = true;
player->paused = false;
player_lock();
pc.state = PLAYER_STATE_PLAY;
player_unlock();
return true;
} else {
player->output_open = false;
/* pause: the user may resume playback as soon as an
audio output becomes available */
player->paused = true;
player_lock();
pc.error = PLAYER_ERROR_AUDIO;
pc.state = PLAYER_STATE_PAUSE;
player_unlock();
return false;
}
}
/**
* The decoder has acknowledged the "START" command (see
* player_wait_for_decoder()). This function checks if the decoder
@@ -301,7 +353,7 @@ player_check_decoder_startup(struct player *player)
decoder_unlock(dc);
if (audio_format_defined(&player->play_audio_format) &&
if (player->output_open &&
!audio_output_all_wait(1))
/* the output devices havn't finished playing
all chunks yet - wait for that */
@@ -315,23 +367,12 @@ player_check_decoder_startup(struct player *player)
player->play_audio_format = dc->out_audio_format;
player->decoder_starting = false;
if (!player->paused &&
!audio_output_all_open(&dc->out_audio_format,
player_buffer)) {
if (!player->paused && !player_open_output(player)) {
char *uri = song_get_uri(dc->song);
g_warning("problems opening audio device "
"while playing \"%s\"", uri);
g_free(uri);
player_lock();
pc.error = PLAYER_ERROR_AUDIO;
/* pause: the user may resume playback as soon
as an audio output becomes available */
pc.state = PLAYER_STATE_PAUSE;
player_unlock();
player->paused = true;
return true;
}
@@ -356,6 +397,7 @@ player_check_decoder_startup(struct player *player)
static bool
player_send_silence(struct player *player)
{
assert(player->output_open);
assert(audio_format_defined(&player->play_audio_format));
struct music_chunk *chunk = music_buffer_allocate(player_buffer);
@@ -510,18 +552,9 @@ static void player_process_command(struct player *player)
yet - don't open the audio device yet */
player_lock();
pc.state = PLAYER_STATE_PLAY;
} else if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
/* unpaused, continue playing */
player_lock();
pc.state = PLAYER_STATE_PLAY;
} else {
/* the audio device has failed - rollback to
pause mode */
pc.error = PLAYER_ERROR_AUDIO;
player->paused = true;
player_open_output(player);
player_lock();
}
@@ -558,8 +591,7 @@ static void player_process_command(struct player *player)
break;
case PLAYER_COMMAND_REFRESH:
if (audio_format_defined(&player->play_audio_format) &&
!player->paused) {
if (player->output_open && !player->paused) {
player_unlock();
audio_output_all_check();
player_lock();
@@ -810,6 +842,7 @@ static void do_play(struct decoder_control *dc)
.decoder_starting = false,
.paused = false,
.queued = true,
.output_open = false,
.song = NULL,
.xfade = XFADE_UNKNOWN,
.cross_fading = false,
@@ -833,6 +866,10 @@ static void do_play(struct decoder_control *dc)
}
player_lock();
if (pc.command == PLAYER_COMMAND_SEEK)
player.elapsed_time = pc.seek_where;
pc.state = PLAYER_STATE_PLAY;
player_command_finished_locked();
@@ -858,7 +895,7 @@ static void do_play(struct decoder_control *dc)
/* not enough decoded buffer space yet */
if (!player.paused &&
audio_format_defined(&player.play_audio_format) &&
player.output_open &&
audio_output_all_check() < 4 &&
!player_send_silence(&player))
break;
@@ -881,16 +918,6 @@ static void do_play(struct decoder_control *dc)
if (!player_check_decoder_startup(&player))
break;
/* seek to the beginning of the range */
const struct song *song = decoder_current_song(dc);
if (song != NULL && song->start_ms > 0 &&
/* we must not send a seek command until
the decoder is initialized
completely */
!player.decoder_starting &&
!dc_seek(dc, song->start_ms / 1000.0))
player_dc_stop(&player);
player_lock();
continue;
}
@@ -973,7 +1000,7 @@ static void do_play(struct decoder_control *dc)
audio_output_all_drain();
break;
}
} else {
} else if (player.output_open) {
/* the decoder is too busy and hasn't provided
new PCM data in time: send silence (if the
output pipe is empty) */
@@ -1021,6 +1048,7 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
while (1) {
switch (pc.command) {
case PLAYER_COMMAND_SEEK:
case PLAYER_COMMAND_QUEUE:
assert(pc.next_song != NULL);
@@ -1034,7 +1062,6 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
/* fall through */
case PLAYER_COMMAND_SEEK:
case PLAYER_COMMAND_PAUSE:
pc.next_song = NULL;
player_command_finished_locked();

View File

@@ -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);

View File

@@ -114,9 +114,7 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
if (g_path_is_absolute(uri)) {
/* XXX fs_charset vs utf8? */
char *prefix = base_uri != NULL
? map_uri_fs(base_uri)
: map_directory_fs(db_get_root());
char *prefix = map_directory_fs(db_get_root());
if (prefix == NULL || !g_str_has_prefix(uri, prefix) ||
uri[strlen(prefix)] != '/') {
@@ -127,6 +125,7 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
return NULL;
}
base_uri = NULL;
uri += strlen(prefix) + 1;
g_free(prefix);
}

View File

@@ -22,6 +22,7 @@
#include "socket_util.h"
#include "fd_util.h"
#include "glib_compat.h"
#include "glib_socket.h"
#include <sys/types.h>
#include <sys/stat.h>
@@ -33,7 +34,6 @@
#include <assert.h>
#ifdef WIN32
#define WINVER 0x0501
#include <ws2tcpip.h>
#include <winsock.h>
#else
@@ -218,7 +218,7 @@ server_socket_open(struct server_socket *ss, GError **error_r)
/* register in the GLib main loop */
GIOChannel *channel = g_io_channel_unix_new(s->fd);
GIOChannel *channel = g_io_channel_new_socket(s->fd);
s->source_id = g_io_add_watch(channel, G_IO_IN,
server_socket_in_event, s);
g_io_channel_unref(channel);
@@ -252,7 +252,7 @@ server_socket_close(struct server_socket *ss)
continue;
g_source_remove(s->source_id);
close(s->fd);
close_socket(s->fd);
s->fd = -1;
}
}

View File

@@ -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 */
@@ -122,7 +121,7 @@ socket_bind_listen(int domain, int type, int protocol,
if (ret < 0) {
g_set_error(error, listen_quark(), errno,
"setsockopt() failed: %s", g_strerror(errno));
close(fd);
close_socket(fd);
return -1;
}
@@ -130,7 +129,7 @@ socket_bind_listen(int domain, int type, int protocol,
if (ret < 0) {
g_set_error(error, listen_quark(), errno,
"%s", g_strerror(errno));
close(fd);
close_socket(fd);
return -1;
}
@@ -138,7 +137,7 @@ socket_bind_listen(int domain, int type, int protocol,
if (ret < 0) {
g_set_error(error, listen_quark(), errno,
"listen() failed: %s", g_strerror(errno));
close(fd);
close_socket(fd);
return -1;
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -25,7 +25,7 @@
#include <stdbool.h>
/**
* Checks whether the specified URI has a schema in the form
* Checks whether the specified URI has a scheme in the form
* "scheme://".
*/
G_GNUC_PURE

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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
View 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);
}