Compare commits

...

44 Commits

Author SHA1 Message Date
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
35 changed files with 452 additions and 131 deletions

1
.gitignore vendored

@@ -39,6 +39,7 @@ tags
*~
.#*
.stgit*
doc/doxygen.conf
doc/protocol.html
doc/protocol
doc/user

@@ -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"'
@@ -243,6 +243,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 \
@@ -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
#

24
NEWS

@@ -1,3 +1,27 @@
ver 0.16.5 (2010/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

@@ -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.5, 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)
@@ -197,7 +214,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 +339,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]),,
@@ -1368,16 +1390,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,10 +1477,9 @@ 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
@@ -1478,6 +1505,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 +1579,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 +1607,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

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

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

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

@@ -214,6 +214,24 @@ align16(void *p, size_t *length_p)
return (char *)p + add;
}
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);
}
static enum decoder_command
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
const AVPacket *packet,
@@ -222,8 +240,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;
@@ -375,8 +392,9 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
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);
@@ -427,7 +445,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,12 +453,16 @@ 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);

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

@@ -78,15 +78,64 @@ 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 (decoder->initial_seek_running)
/* initial seek has already begun - override any other
command */
return true;
if (decoder->initial_seek_pending) {
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 +143,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 +186,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 +202,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 +344,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 +431,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 +462,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) {

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

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

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

@@ -369,6 +369,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;

@@ -21,6 +21,7 @@
#include "event_pipe.h"
#include "fd_util.h"
#include "mpd_error.h"
#include "glib_socket.h"
#include <stdbool.h>
#include <assert.h>
@@ -94,11 +95,7 @@ void event_pipe_init(void)
if (ret < 0)
MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
#ifndef G_OS_WIN32
channel = g_io_channel_unix_new(event_pipe[0]);
#else
channel = g_io_channel_win32_new_fd(event_pipe[0]);
#endif
channel = g_io_channel_new_socket(event_pipe[0]);
g_io_channel_set_encoding(channel, NULL, NULL);
g_io_channel_set_buffered(channel, false);

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

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

40
src/glib_socket.h Normal 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

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

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

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

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

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

@@ -64,26 +64,26 @@ openal_audio_format(struct audio_format *audio_format)
return AL_FORMAT_STEREO16;
if (audio_format->channels == 1)
return AL_FORMAT_MONO16;
break;
/* fall back to mono */
audio_format->channels = 1;
return openal_audio_format(audio_format);
case SAMPLE_FORMAT_S8:
if (audio_format->channels == 2)
return AL_FORMAT_STEREO8;
if (audio_format->channels == 1)
return AL_FORMAT_MONO8;
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

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

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

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

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

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

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

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

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

@@ -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>
@@ -218,7 +219,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 +253,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;
}
}

@@ -122,7 +122,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 +130,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 +138,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;
}

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