Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3041409334 | ||
|
|
71536eb412 | ||
|
|
fe77230d84 | ||
|
|
5ed0eb51d1 | ||
|
|
72a1ca3b99 | ||
|
|
72ff9bd3e6 | ||
|
|
039b354490 | ||
|
|
b2f03e76ff | ||
|
|
63b33b6ec5 | ||
|
|
23670795db | ||
|
|
8ea6c113b5 | ||
|
|
37f026a0a6 | ||
|
|
f67136df19 | ||
|
|
e07073ff28 | ||
|
|
64b0ba6da7 | ||
|
|
99d4ae0c1a | ||
|
|
f185b35088 | ||
|
|
83f6498aac | ||
|
|
525a791987 | ||
|
|
fb19210cfd | ||
|
|
b0b2c5b3e0 | ||
|
|
29742d23d3 | ||
|
|
c476819cb1 | ||
|
|
77a56c7c5a | ||
|
|
46deb7ca82 | ||
|
|
b03f9ece05 | ||
|
|
1d8840412f | ||
|
|
df1152ee0f | ||
|
|
79435dbdec | ||
|
|
27206368da | ||
|
|
443e96381a | ||
|
|
1cbba4fc59 | ||
|
|
344b6dd179 | ||
|
|
d8c829fa0c | ||
|
|
2ed870c854 | ||
|
|
ce35ba9ac9 | ||
|
|
724a59aaf7 | ||
|
|
42d8c2981f | ||
|
|
9aa91e0f17 | ||
|
|
5aabee8996 | ||
|
|
48a84ca23e | ||
|
|
c345c5ebae | ||
|
|
5cf4ce9318 | ||
|
|
5469941f2b |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,6 +39,7 @@ tags
|
|||||||
*~
|
*~
|
||||||
.#*
|
.#*
|
||||||
.stgit*
|
.stgit*
|
||||||
|
doc/doxygen.conf
|
||||||
doc/protocol.html
|
doc/protocol.html
|
||||||
doc/protocol
|
doc/protocol
|
||||||
doc/user
|
doc/user
|
||||||
|
|||||||
12
Makefile.am
12
Makefile.am
@@ -1,7 +1,7 @@
|
|||||||
ACLOCAL_AMFLAGS = -I m4
|
ACLOCAL_AMFLAGS = -I m4
|
||||||
AUTOMAKE_OPTIONS = foreign 1.10 dist-bzip2 subdir-objects
|
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"'
|
AM_CPPFLAGS += -DSYSTEM_CONFIG_FILE_LOCATION='"$(sysconfdir)/mpd.conf"'
|
||||||
|
|
||||||
@@ -243,6 +243,7 @@ src_mpd_SOURCES = \
|
|||||||
$(OUTPUT_API_SRC) $(OUTPUT_SRC) \
|
$(OUTPUT_API_SRC) $(OUTPUT_SRC) \
|
||||||
$(MIXER_API_SRC) $(MIXER_SRC) \
|
$(MIXER_API_SRC) $(MIXER_SRC) \
|
||||||
$(FILTER_SRC) \
|
$(FILTER_SRC) \
|
||||||
|
src/glib_socket.h \
|
||||||
src/notify.c \
|
src/notify.c \
|
||||||
src/audio.c \
|
src/audio.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
@@ -801,6 +802,15 @@ FILTER_SRC = \
|
|||||||
src/filter/volume_filter_plugin.c
|
src/filter/volume_filter_plugin.c
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# systemd unit
|
||||||
|
#
|
||||||
|
|
||||||
|
if HAVE_SYSTEMD
|
||||||
|
systemdsystemunit_DATA = \
|
||||||
|
mpd.service
|
||||||
|
endif
|
||||||
|
|
||||||
#
|
#
|
||||||
# Sparse code analysis
|
# Sparse code analysis
|
||||||
#
|
#
|
||||||
|
|||||||
24
NEWS
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)
|
ver 0.16.4 (2011/09/01)
|
||||||
* don't abort configure when avahi is not found
|
* don't abort configure when avahi is not found
|
||||||
* auto-detect libmad without pkg-config
|
* auto-detect libmad without pkg-config
|
||||||
|
|||||||
49
configure.ac
49
configure.ac
@@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.60)
|
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])
|
AC_CONFIG_SRCDIR([src/main.c])
|
||||||
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
|
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
|
||||||
AM_CONFIG_HEADER(config.h)
|
AM_CONFIG_HEADER(config.h)
|
||||||
@@ -33,11 +33,28 @@ fi
|
|||||||
AC_PROG_INSTALL
|
AC_PROG_INSTALL
|
||||||
AC_PROG_MAKE_SET
|
AC_PROG_MAKE_SET
|
||||||
PKG_PROG_PKG_CONFIG
|
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 ---------------------------------------------------------------------------
|
||||||
dnl Declare Variables
|
dnl Declare Variables
|
||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
|
AC_SUBST(AM_CPPFLAGS,"")
|
||||||
AC_SUBST(AM_CFLAGS,"")
|
AC_SUBST(AM_CFLAGS,"")
|
||||||
|
AC_SUBST(AM_CXXFLAGS,"")
|
||||||
|
|
||||||
AC_SUBST(MPD_LIBS)
|
AC_SUBST(MPD_LIBS)
|
||||||
AC_SUBST(MPD_CFLAGS)
|
AC_SUBST(MPD_CFLAGS)
|
||||||
@@ -197,7 +214,7 @@ AC_ARG_ENABLE(httpd-output,
|
|||||||
|
|
||||||
AC_ARG_ENABLE(id3,
|
AC_ARG_ENABLE(id3,
|
||||||
AS_HELP_STRING([--enable-id3],
|
AS_HELP_STRING([--enable-id3],
|
||||||
[disable id3 support]),,
|
[enable id3 support]),,
|
||||||
enable_id3=auto)
|
enable_id3=auto)
|
||||||
|
|
||||||
AC_ARG_ENABLE(inotify,
|
AC_ARG_ENABLE(inotify,
|
||||||
@@ -322,6 +339,11 @@ AC_ARG_ENABLE(sndfile,
|
|||||||
[enable sndfile support]),,
|
[enable sndfile support]),,
|
||||||
enable_sndfile=auto)
|
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,
|
AC_ARG_ENABLE(sqlite,
|
||||||
AS_HELP_STRING([--enable-sqlite],
|
AS_HELP_STRING([--enable-sqlite],
|
||||||
[enable support for the SQLite database]),,
|
[enable support for the SQLite database]),,
|
||||||
@@ -1368,16 +1390,22 @@ fi
|
|||||||
AM_CONDITIONAL(HAVE_SHOUT, test x$enable_shout = xyes)
|
AM_CONDITIONAL(HAVE_SHOUT, test x$enable_shout = xyes)
|
||||||
|
|
||||||
dnl --------------------------------- Solaris ---------------------------------
|
dnl --------------------------------- Solaris ---------------------------------
|
||||||
case "$host_os" in
|
|
||||||
|
if test x$enable_solaris_output = xauto; then
|
||||||
|
case "$host_os" in
|
||||||
solaris*)
|
solaris*)
|
||||||
AC_DEFINE(ENABLE_SOLARIS_OUTPUT, 1, [Define to enable Solaris /dev/audio support])
|
|
||||||
enable_solaris_output=yes
|
enable_solaris_output=yes
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
enable_solaris_output=no
|
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)
|
AM_CONDITIONAL(ENABLE_SOLARIS_OUTPUT, test x$enable_solaris_output = xyes)
|
||||||
|
|
||||||
@@ -1449,10 +1477,9 @@ dnl CFLAGS
|
|||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
|
|
||||||
dnl ---------------------------------- debug ----------------------------------
|
dnl ---------------------------------- debug ----------------------------------
|
||||||
#if test "x$enable_debug" = xno; then
|
if test "x$enable_debug" = xno; then
|
||||||
# don't set NDEBUG for now, until MPD is stable
|
AM_CPPFLAGS="$AM_CPPFLAGS -DNDEBUG"
|
||||||
#AM_CFLAGS="$AM_CFLAGS -DNDEBUG"
|
fi
|
||||||
#fi
|
|
||||||
|
|
||||||
dnl ----------------------------------- GCC -----------------------------------
|
dnl ----------------------------------- GCC -----------------------------------
|
||||||
if test x$GCC = xyes
|
if test x$GCC = xyes
|
||||||
@@ -1478,6 +1505,7 @@ fi
|
|||||||
dnl ---------------------------- warnings as errors ---------------------------
|
dnl ---------------------------- warnings as errors ---------------------------
|
||||||
if test "x$enable_werror" = xyes; then
|
if test "x$enable_werror" = xyes; then
|
||||||
AM_CFLAGS="$AM_CFLAGS -Werror -pedantic-errors"
|
AM_CFLAGS="$AM_CFLAGS -Werror -pedantic-errors"
|
||||||
|
AM_CXXFLAGS="$AM_CXXFLAGS -Werror"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
@@ -1551,7 +1579,7 @@ results(pulse, [PulseAudio])
|
|||||||
results(mvp, [Media MVP])
|
results(mvp, [Media MVP])
|
||||||
results(shout, [SHOUTcast])
|
results(shout, [SHOUTcast])
|
||||||
printf '\n\t'
|
printf '\n\t'
|
||||||
results(solaris, [Solaris])
|
results(solaris_output, [Solaris])
|
||||||
results(winmm_output, [WinMM])
|
results(winmm_output, [WinMM])
|
||||||
|
|
||||||
if
|
if
|
||||||
@@ -1579,5 +1607,6 @@ dnl ---------------------------------------------------------------------------
|
|||||||
dnl Generate files
|
dnl Generate files
|
||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
AC_OUTPUT(Makefile)
|
AC_OUTPUT(Makefile)
|
||||||
|
AC_OUTPUT(mpd.service)
|
||||||
|
|
||||||
echo 'MPD is ready for compilation, type "make" to begin.'
|
echo 'MPD is ready for compilation, type "make" to begin.'
|
||||||
|
|||||||
9
mpd.service.in
Normal file
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 "config.h"
|
||||||
#include "client_internal.h"
|
#include "client_internal.h"
|
||||||
|
#include "fd_util.h"
|
||||||
#include "fifo_buffer.h"
|
#include "fifo_buffer.h"
|
||||||
#include "socket_util.h"
|
#include "socket_util.h"
|
||||||
#include "permission.h"
|
#include "permission.h"
|
||||||
|
#include "glib_socket.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sys/types.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);
|
progname, hostaddr);
|
||||||
|
|
||||||
g_free(hostaddr);
|
g_free(hostaddr);
|
||||||
close(fd);
|
close_socket(fd);
|
||||||
return;
|
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()) {
|
if (client_list_is_full()) {
|
||||||
g_warning("Max Connections Reached!");
|
g_warning("Max Connections Reached!");
|
||||||
close(fd);
|
close_socket(fd);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
client = g_new0(struct client, 1);
|
client = g_new0(struct client, 1);
|
||||||
|
|
||||||
#ifndef G_OS_WIN32
|
client->channel = g_io_channel_new_socket(fd);
|
||||||
client->channel = g_io_channel_unix_new(fd);
|
|
||||||
#else
|
|
||||||
client->channel = g_io_channel_win32_new_socket(fd);
|
|
||||||
#endif
|
|
||||||
/* GLib is responsible for closing the file descriptor */
|
/* GLib is responsible for closing the file descriptor */
|
||||||
g_io_channel_set_close_on_unref(client->channel, true);
|
g_io_channel_set_close_on_unref(client->channel, true);
|
||||||
/* NULL encoding means the stream is binary safe; the MPD
|
/* NULL encoding means the stream is binary safe; the MPD
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <af_vfs.h>
|
#include <af_vfs.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "audiofile"
|
#define G_LOG_DOMAIN "audiofile"
|
||||||
|
|||||||
@@ -214,6 +214,24 @@ align16(void *p, size_t *length_p)
|
|||||||
return (char *)p + add;
|
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
|
static enum decoder_command
|
||||||
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
||||||
const AVPacket *packet,
|
const AVPacket *packet,
|
||||||
@@ -222,8 +240,7 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
|||||||
{
|
{
|
||||||
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
|
if (packet->pts != (int64_t)AV_NOPTS_VALUE)
|
||||||
decoder_timestamp(decoder,
|
decoder_timestamp(decoder,
|
||||||
av_rescale_q(packet->pts, *time_base,
|
time_from_ffmpeg(packet->pts, *time_base));
|
||||||
(AVRational){1, 1}));
|
|
||||||
|
|
||||||
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
AVPacket packet2 = *packet;
|
AVPacket packet2 = *packet;
|
||||||
@@ -375,8 +392,9 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVCodecContext *codec_context =
|
AVStream *av_stream = format_context->streams[audio_stream];
|
||||||
format_context->streams[audio_stream]->codec;
|
|
||||||
|
AVCodecContext *codec_context = av_stream->codec;
|
||||||
if (codec_context->codec_name[0] != 0)
|
if (codec_context->codec_name[0] != 0)
|
||||||
g_debug("codec '%s'", codec_context->codec_name);
|
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)
|
if (packet.stream_index == audio_stream)
|
||||||
cmd = ffmpeg_send_packet(decoder, input,
|
cmd = ffmpeg_send_packet(decoder, input,
|
||||||
&packet, codec_context,
|
&packet, codec_context,
|
||||||
&format_context->streams[audio_stream]->time_base);
|
&av_stream->time_base);
|
||||||
else
|
else
|
||||||
cmd = decoder_get_command(decoder);
|
cmd = decoder_get_command(decoder);
|
||||||
|
|
||||||
@@ -435,12 +453,16 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
|
|
||||||
if (cmd == DECODE_COMMAND_SEEK) {
|
if (cmd == DECODE_COMMAND_SEEK) {
|
||||||
int64_t where =
|
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);
|
decoder_seek_error(decoder);
|
||||||
else
|
else {
|
||||||
|
avcodec_flush_buffers(codec_context);
|
||||||
decoder_command_finished(decoder);
|
decoder_command_finished(decoder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (cmd != DECODE_COMMAND_STOP);
|
} while (cmd != DECODE_COMMAND_STOP);
|
||||||
|
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT) {
|
if (is_float) {
|
||||||
format_samples = format_samples_float;
|
format_samples = format_samples_float;
|
||||||
} else {
|
} else {
|
||||||
format_samples = format_samples_int;
|
format_samples = format_samples_int;
|
||||||
|
|||||||
@@ -78,15 +78,64 @@ decoder_initialized(struct decoder *decoder,
|
|||||||
&af_string));
|
&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;
|
const struct decoder_control *dc = decoder->dc;
|
||||||
|
|
||||||
assert(dc->pipe != NULL);
|
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;
|
return dc->command;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum decoder_command
|
||||||
|
decoder_get_command(struct decoder *decoder)
|
||||||
|
{
|
||||||
|
return decoder_get_virtual_command(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
decoder_command_finished(struct decoder *decoder)
|
decoder_command_finished(struct decoder *decoder)
|
||||||
{
|
{
|
||||||
@@ -94,11 +143,24 @@ decoder_command_finished(struct decoder *decoder)
|
|||||||
|
|
||||||
decoder_lock(dc);
|
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 ||
|
assert(dc->command != DECODE_COMMAND_SEEK ||
|
||||||
|
decoder->initial_seek_running ||
|
||||||
dc->seek_error || decoder->seeking);
|
dc->seek_error || decoder->seeking);
|
||||||
assert(dc->pipe != NULL);
|
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) {
|
if (decoder->seeking) {
|
||||||
decoder->seeking = false;
|
decoder->seeking = false;
|
||||||
|
|
||||||
@@ -124,9 +186,13 @@ double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
|
|||||||
{
|
{
|
||||||
const struct decoder_control *dc = decoder->dc;
|
const struct decoder_control *dc = decoder->dc;
|
||||||
|
|
||||||
assert(dc->command == DECODE_COMMAND_SEEK);
|
|
||||||
assert(dc->pipe != NULL);
|
assert(dc->pipe != NULL);
|
||||||
|
|
||||||
|
if (decoder->initial_seek_running)
|
||||||
|
return dc->start_ms / 1000.;
|
||||||
|
|
||||||
|
assert(dc->command == DECODE_COMMAND_SEEK);
|
||||||
|
|
||||||
decoder->seeking = true;
|
decoder->seeking = true;
|
||||||
|
|
||||||
return dc->seek_where;
|
return dc->seek_where;
|
||||||
@@ -136,9 +202,17 @@ void decoder_seek_error(struct decoder * decoder)
|
|||||||
{
|
{
|
||||||
struct decoder_control *dc = decoder->dc;
|
struct decoder_control *dc = decoder->dc;
|
||||||
|
|
||||||
assert(dc->command == DECODE_COMMAND_SEEK);
|
|
||||||
assert(dc->pipe != NULL);
|
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;
|
dc->seek_error = true;
|
||||||
decoder->seeking = false;
|
decoder->seeking = false;
|
||||||
|
|
||||||
@@ -270,7 +344,7 @@ decoder_data(struct decoder *decoder,
|
|||||||
assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
|
assert(length % audio_format_frame_size(&dc->in_audio_format) == 0);
|
||||||
|
|
||||||
decoder_lock(dc);
|
decoder_lock(dc);
|
||||||
cmd = dc->command;
|
cmd = decoder_get_virtual_command(decoder);
|
||||||
decoder_unlock(dc);
|
decoder_unlock(dc);
|
||||||
|
|
||||||
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
|
if (cmd == DECODE_COMMAND_STOP || cmd == DECODE_COMMAND_SEEK ||
|
||||||
@@ -357,8 +431,8 @@ decoder_data(struct decoder *decoder,
|
|||||||
decoder->timestamp += (double)nbytes /
|
decoder->timestamp += (double)nbytes /
|
||||||
audio_format_time_to_size(&dc->out_audio_format);
|
audio_format_time_to_size(&dc->out_audio_format);
|
||||||
|
|
||||||
if (dc->song->end_ms > 0 &&
|
if (dc->end_ms > 0 &&
|
||||||
decoder->timestamp >= dc->song->end_ms / 1000.0)
|
decoder->timestamp >= dc->end_ms / 1000.0)
|
||||||
/* the end of this range has been reached:
|
/* the end of this range has been reached:
|
||||||
stop decoding */
|
stop decoding */
|
||||||
return DECODE_COMMAND_STOP;
|
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);
|
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 */
|
/* send tag to music pipe */
|
||||||
|
|
||||||
if (decoder->stream_tag != NULL) {
|
if (decoder->stream_tag != NULL) {
|
||||||
|
|||||||
@@ -102,6 +102,7 @@ dc_command_async(struct decoder_control *dc, enum decoder_command cmd)
|
|||||||
|
|
||||||
void
|
void
|
||||||
dc_start(struct decoder_control *dc, struct song *song,
|
dc_start(struct decoder_control *dc, struct song *song,
|
||||||
|
unsigned start_ms, unsigned end_ms,
|
||||||
struct music_buffer *buffer, struct music_pipe *pipe)
|
struct music_buffer *buffer, struct music_pipe *pipe)
|
||||||
{
|
{
|
||||||
assert(song != NULL);
|
assert(song != NULL);
|
||||||
@@ -110,6 +111,8 @@ dc_start(struct decoder_control *dc, struct song *song,
|
|||||||
assert(music_pipe_empty(pipe));
|
assert(music_pipe_empty(pipe));
|
||||||
|
|
||||||
dc->song = song;
|
dc->song = song;
|
||||||
|
dc->start_ms = start_ms;
|
||||||
|
dc->end_ms = end_ms;
|
||||||
dc->buffer = buffer;
|
dc->buffer = buffer;
|
||||||
dc->pipe = pipe;
|
dc->pipe = pipe;
|
||||||
dc_command(dc, DECODE_COMMAND_START);
|
dc_command(dc, DECODE_COMMAND_START);
|
||||||
|
|||||||
@@ -79,6 +79,23 @@ struct decoder_control {
|
|||||||
*/
|
*/
|
||||||
const struct song *song;
|
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;
|
float total_time;
|
||||||
|
|
||||||
/** the #music_chunk allocator */
|
/** the #music_chunk allocator */
|
||||||
@@ -225,11 +242,14 @@ dc_command_wait(struct decoder_control *dc);
|
|||||||
*
|
*
|
||||||
* @param the decoder
|
* @param the decoder
|
||||||
* @param song the song to be decoded
|
* @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
|
* @param pipe the pipe which receives the decoded chunks (owned by
|
||||||
* the caller)
|
* the caller)
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
dc_start(struct decoder_control *dc, struct song *song,
|
dc_start(struct decoder_control *dc, struct song *song,
|
||||||
|
unsigned start_ms, unsigned end_ms,
|
||||||
struct music_buffer *buffer, struct music_pipe *pipe);
|
struct music_buffer *buffer, struct music_pipe *pipe);
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
@@ -36,6 +36,25 @@ struct decoder {
|
|||||||
*/
|
*/
|
||||||
double timestamp;
|
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;
|
bool seeking;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -369,6 +369,8 @@ decoder_run_song(struct decoder_control *dc,
|
|||||||
{
|
{
|
||||||
struct decoder decoder = {
|
struct decoder decoder = {
|
||||||
.dc = dc,
|
.dc = dc,
|
||||||
|
.initial_seek_pending = dc->start_ms > 0,
|
||||||
|
.initial_seek_running = false,
|
||||||
};
|
};
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "event_pipe.h"
|
#include "event_pipe.h"
|
||||||
#include "fd_util.h"
|
#include "fd_util.h"
|
||||||
#include "mpd_error.h"
|
#include "mpd_error.h"
|
||||||
|
#include "glib_socket.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -94,11 +95,7 @@ void event_pipe_init(void)
|
|||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
|
MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
|
||||||
|
|
||||||
#ifndef G_OS_WIN32
|
channel = g_io_channel_new_socket(event_pipe[0]);
|
||||||
channel = g_io_channel_unix_new(event_pipe[0]);
|
|
||||||
#else
|
|
||||||
channel = g_io_channel_win32_new_fd(event_pipe[0]);
|
|
||||||
#endif
|
|
||||||
g_io_channel_set_encoding(channel, NULL, NULL);
|
g_io_channel_set_encoding(channel, NULL, NULL);
|
||||||
g_io_channel_set_buffered(channel, false);
|
g_io_channel_set_buffered(channel, false);
|
||||||
|
|
||||||
|
|||||||
@@ -304,3 +304,13 @@ inotify_init_cloexec(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int
|
||||||
|
close_socket(int fd)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
return closesocket(fd);
|
||||||
|
#else
|
||||||
|
return close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -36,6 +36,8 @@
|
|||||||
#ifndef FD_UTIL_H
|
#ifndef FD_UTIL_H
|
||||||
#define FD_UTIL_H
|
#define FD_UTIL_H
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
@@ -120,6 +122,8 @@ recvmsg_cloexec(int sockfd, struct msghdr *msg, int flags);
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_INOTIFY_INIT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for inotify_init(), which sets the CLOEXEC flag (atomically
|
* Wrapper for inotify_init(), which sets the CLOEXEC flag (atomically
|
||||||
* if supported by the OS).
|
* if supported by the OS).
|
||||||
@@ -128,3 +132,11 @@ int
|
|||||||
inotify_init_cloexec(void);
|
inotify_init_cloexec(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Portable wrapper for close(); use closesocket() on WIN32/WinSock.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
close_socket(int fd);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|||||||
40
src/glib_socket.h
Normal file
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);
|
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(dest != src);
|
||||||
assert(src->mime == NULL || dest->mime != src->mime);
|
assert(src->mime == NULL || dest->mime != src->mime);
|
||||||
|
|
||||||
|
bool dest_ready = dest->ready;
|
||||||
|
|
||||||
dest->ready = src->ready;
|
dest->ready = src->ready;
|
||||||
dest->seekable = src->seekable;
|
dest->seekable = src->seekable;
|
||||||
dest->size = src->size;
|
dest->size = src->size;
|
||||||
dest->offset = src->offset;
|
dest->offset = src->offset;
|
||||||
|
|
||||||
if (src->mime != NULL) {
|
if (!dest_ready && src->ready) {
|
||||||
g_free(dest->mime);
|
g_free(dest->mime);
|
||||||
dest->mime = g_strdup(src->mime);
|
dest->mime = g_strdup(src->mime);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,6 +186,9 @@ static snd_pcm_format_t
|
|||||||
get_bitformat(enum sample_format sample_format)
|
get_bitformat(enum sample_format sample_format)
|
||||||
{
|
{
|
||||||
switch (sample_format) {
|
switch (sample_format) {
|
||||||
|
case SAMPLE_FORMAT_UNDEFINED:
|
||||||
|
return SND_PCM_FORMAT_UNKNOWN;
|
||||||
|
|
||||||
case SAMPLE_FORMAT_S8:
|
case SAMPLE_FORMAT_S8:
|
||||||
return SND_PCM_FORMAT_S8;
|
return SND_PCM_FORMAT_S8;
|
||||||
|
|
||||||
@@ -202,10 +205,10 @@ get_bitformat(enum sample_format sample_format)
|
|||||||
|
|
||||||
case SAMPLE_FORMAT_S32:
|
case SAMPLE_FORMAT_S32:
|
||||||
return SND_PCM_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
|
static snd_pcm_format_t
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "page.h"
|
#include "page.h"
|
||||||
#include "icy_server.h"
|
#include "icy_server.h"
|
||||||
#include "glib_compat.h"
|
#include "glib_compat.h"
|
||||||
|
#include "glib_socket.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@@ -459,11 +460,7 @@ httpd_client_new(struct httpd_output *httpd, int fd, bool metadata_supported)
|
|||||||
|
|
||||||
client->httpd = httpd;
|
client->httpd = httpd;
|
||||||
|
|
||||||
#ifndef G_OS_WIN32
|
client->channel = g_io_channel_new_socket(fd);
|
||||||
client->channel = g_io_channel_unix_new(fd);
|
|
||||||
#else
|
|
||||||
client->channel = g_io_channel_win32_new_socket(fd);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* GLib is responsible for closing the file descriptor */
|
/* GLib is responsible for closing the file descriptor */
|
||||||
g_io_channel_set_close_on_unref(client->channel, true);
|
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",
|
g_warning("libwrap refused connection (libwrap=%s) from %s",
|
||||||
progname, hostaddr);
|
progname, hostaddr);
|
||||||
g_free(hostaddr);
|
g_free(hostaddr);
|
||||||
close(fd);
|
close_socket(fd);
|
||||||
g_mutex_unlock(httpd->mutex);
|
g_mutex_unlock(httpd->mutex);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -222,7 +222,7 @@ httpd_listen_in_event(int fd, const struct sockaddr *address,
|
|||||||
httpd->clients_cnt < httpd->clients_max))
|
httpd->clients_cnt < httpd->clients_max))
|
||||||
httpd_client_add(httpd, fd);
|
httpd_client_add(httpd, fd);
|
||||||
else
|
else
|
||||||
close(fd);
|
close_socket(fd);
|
||||||
} else if (fd < 0 && errno != EINTR) {
|
} else if (fd < 0 && errno != EINTR) {
|
||||||
g_warning("accept() failed: %s", g_strerror(errno));
|
g_warning("accept() failed: %s", g_strerror(errno));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,26 +64,26 @@ openal_audio_format(struct audio_format *audio_format)
|
|||||||
return AL_FORMAT_STEREO16;
|
return AL_FORMAT_STEREO16;
|
||||||
if (audio_format->channels == 1)
|
if (audio_format->channels == 1)
|
||||||
return AL_FORMAT_MONO16;
|
return AL_FORMAT_MONO16;
|
||||||
break;
|
|
||||||
|
/* fall back to mono */
|
||||||
|
audio_format->channels = 1;
|
||||||
|
return openal_audio_format(audio_format);
|
||||||
|
|
||||||
case SAMPLE_FORMAT_S8:
|
case SAMPLE_FORMAT_S8:
|
||||||
if (audio_format->channels == 2)
|
if (audio_format->channels == 2)
|
||||||
return AL_FORMAT_STEREO8;
|
return AL_FORMAT_STEREO8;
|
||||||
if (audio_format->channels == 1)
|
if (audio_format->channels == 1)
|
||||||
return AL_FORMAT_MONO8;
|
return AL_FORMAT_MONO8;
|
||||||
break;
|
|
||||||
|
/* fall back to mono */
|
||||||
|
audio_format->channels = 1;
|
||||||
|
return openal_audio_format(audio_format);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
/* fall back to 16 bit */
|
/* fall back to 16 bit */
|
||||||
audio_format->format = SAMPLE_FORMAT_S16;
|
audio_format->format = SAMPLE_FORMAT_S16;
|
||||||
if (audio_format->channels == 2)
|
return openal_audio_format(audio_format);
|
||||||
return AL_FORMAT_STEREO16;
|
|
||||||
if (audio_format->channels == 1)
|
|
||||||
return AL_FORMAT_MONO16;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ pulse_output_set_mixer(struct pulse_output *po, struct pulse_mixer *pm)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
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(po != NULL);
|
||||||
assert(pm != NULL);
|
assert(pm != NULL);
|
||||||
@@ -247,6 +248,8 @@ pulse_output_delete_stream(struct pulse_output *po)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees and clears the context.
|
* Frees and clears the context.
|
||||||
|
*
|
||||||
|
* Caller must lock the main loop.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
pulse_output_delete_context(struct pulse_output *po)
|
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.
|
* Create, set up and connect a context.
|
||||||
*
|
*
|
||||||
|
* Caller must lock the main loop.
|
||||||
|
*
|
||||||
* @return true on success, false on error
|
* @return true on success, false on error
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
@@ -355,12 +360,8 @@ pulse_output_enable(void *data, GError **error_r)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_threaded_mainloop_unlock(po->mainloop);
|
|
||||||
|
|
||||||
/* create the libpulse context and connect it */
|
/* create the libpulse context and connect it */
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
|
||||||
|
|
||||||
if (!pulse_output_setup_context(po, error_r)) {
|
if (!pulse_output_setup_context(po, error_r)) {
|
||||||
pa_threaded_mainloop_unlock(po->mainloop);
|
pa_threaded_mainloop_unlock(po->mainloop);
|
||||||
pa_threaded_mainloop_stop(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
|
* Check if the context is (already) connected, and waits if not. If
|
||||||
* the context has been disconnected, retry to connect.
|
* the context has been disconnected, retry to connect.
|
||||||
*
|
*
|
||||||
|
* Caller must lock the main loop.
|
||||||
|
*
|
||||||
* @return true on success, false on error
|
* @return true on success, false on error
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
@@ -401,8 +404,6 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
|||||||
|
|
||||||
pa_context_state_t state;
|
pa_context_state_t state;
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
|
||||||
|
|
||||||
if (po->context == NULL && !pulse_output_setup_context(po, error_r))
|
if (po->context == NULL && !pulse_output_setup_context(po, error_r))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -411,7 +412,6 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
|||||||
switch (state) {
|
switch (state) {
|
||||||
case PA_CONTEXT_READY:
|
case PA_CONTEXT_READY:
|
||||||
/* nothing to do */
|
/* nothing to do */
|
||||||
pa_threaded_mainloop_unlock(po->mainloop);
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case PA_CONTEXT_UNCONNECTED:
|
case PA_CONTEXT_UNCONNECTED:
|
||||||
@@ -422,7 +422,6 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
|||||||
"failed to connect: %s",
|
"failed to connect: %s",
|
||||||
pa_strerror(pa_context_errno(po->context)));
|
pa_strerror(pa_context_errno(po->context)));
|
||||||
pulse_output_delete_context(po);
|
pulse_output_delete_context(po);
|
||||||
pa_threaded_mainloop_unlock(po->mainloop);
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case PA_CONTEXT_CONNECTING:
|
case PA_CONTEXT_CONNECTING:
|
||||||
@@ -505,6 +504,8 @@ pulse_output_open(void *data, struct audio_format *audio_format,
|
|||||||
|
|
||||||
assert(po->mainloop != NULL);
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
|
pa_threaded_mainloop_lock(po->mainloop);
|
||||||
|
|
||||||
if (po->context != NULL) {
|
if (po->context != NULL) {
|
||||||
switch (pa_context_get_state(po->context)) {
|
switch (pa_context_get_state(po->context)) {
|
||||||
case PA_CONTEXT_UNCONNECTED:
|
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;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* MPD doesn't support the other pulseaudio sample formats, so
|
/* MPD doesn't support the other pulseaudio sample formats, so
|
||||||
we just force MPD to send us everything as 16 bit */
|
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.rate = audio_format->sample_rate;
|
||||||
ss.channels = audio_format->channels;
|
ss.channels = audio_format->channels;
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
|
||||||
|
|
||||||
/* create a stream .. */
|
/* create a stream .. */
|
||||||
|
|
||||||
po->stream = pa_stream_new(po->context, po->name, &ss, NULL);
|
po->stream = pa_stream_new(po->context, po->name, &ss, NULL);
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#include <sys/audio.h>
|
|
||||||
#include <sys/stropts.h>
|
#include <sys/stropts.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
@@ -31,6 +30,25 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.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
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "solaris_output"
|
#define G_LOG_DOMAIN "solaris_output"
|
||||||
|
|
||||||
|
|||||||
@@ -102,9 +102,6 @@ audio_output_disable(struct audio_output *ao)
|
|||||||
g_mutex_unlock(ao->mutex);
|
g_mutex_unlock(ao->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
audio_output_close_locked(struct audio_output *ao);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Object must be locked (and unlocked) by the caller.
|
* 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);
|
src_buffer, src_size, &len);
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
g_set_error(error_r, pcm_convert_quark(), 0,
|
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));
|
sample_format_to_string(src_format->format));
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ pcm_convert_16_to_24(int32_t *out, const int16_t *in,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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)
|
unsigned num_samples)
|
||||||
{
|
{
|
||||||
while (num_samples > 0) {
|
while (num_samples > 0) {
|
||||||
@@ -197,7 +197,7 @@ pcm_convert_to_24(struct pcm_buffer *buffer,
|
|||||||
*dest_size_r = num_samples * sizeof(*dest);
|
*dest_size_r = num_samples * sizeof(*dest);
|
||||||
dest = pcm_buffer_get(buffer, *dest_size_r);
|
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);
|
num_samples);
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,9 +309,6 @@ pc_seek(struct song *song, float seek_time)
|
|||||||
{
|
{
|
||||||
assert(song != NULL);
|
assert(song != NULL);
|
||||||
|
|
||||||
if (pc.state == PLAYER_STATE_STOP)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
player_lock();
|
player_lock();
|
||||||
pc.next_song = song;
|
pc.next_song = song;
|
||||||
pc.seek_where = seek_time;
|
pc.seek_where = seek_time;
|
||||||
|
|||||||
@@ -73,6 +73,14 @@ struct player {
|
|||||||
*/
|
*/
|
||||||
bool queued;
|
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
|
* 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(player->queued || pc.command == PLAYER_COMMAND_SEEK);
|
||||||
assert(pc.next_song != NULL);
|
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;
|
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
|
* The decoder has acknowledged the "START" command (see
|
||||||
* player_wait_for_decoder()). This function checks if the decoder
|
* player_wait_for_decoder()). This function checks if the decoder
|
||||||
@@ -301,7 +353,7 @@ player_check_decoder_startup(struct player *player)
|
|||||||
|
|
||||||
decoder_unlock(dc);
|
decoder_unlock(dc);
|
||||||
|
|
||||||
if (audio_format_defined(&player->play_audio_format) &&
|
if (player->output_open &&
|
||||||
!audio_output_all_wait(1))
|
!audio_output_all_wait(1))
|
||||||
/* the output devices havn't finished playing
|
/* the output devices havn't finished playing
|
||||||
all chunks yet - wait for that */
|
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->play_audio_format = dc->out_audio_format;
|
||||||
player->decoder_starting = false;
|
player->decoder_starting = false;
|
||||||
|
|
||||||
if (!player->paused &&
|
if (!player->paused && !player_open_output(player)) {
|
||||||
!audio_output_all_open(&dc->out_audio_format,
|
|
||||||
player_buffer)) {
|
|
||||||
char *uri = song_get_uri(dc->song);
|
char *uri = song_get_uri(dc->song);
|
||||||
g_warning("problems opening audio device "
|
g_warning("problems opening audio device "
|
||||||
"while playing \"%s\"", uri);
|
"while playing \"%s\"", uri);
|
||||||
g_free(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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -356,6 +397,7 @@ player_check_decoder_startup(struct player *player)
|
|||||||
static bool
|
static bool
|
||||||
player_send_silence(struct player *player)
|
player_send_silence(struct player *player)
|
||||||
{
|
{
|
||||||
|
assert(player->output_open);
|
||||||
assert(audio_format_defined(&player->play_audio_format));
|
assert(audio_format_defined(&player->play_audio_format));
|
||||||
|
|
||||||
struct music_chunk *chunk = music_buffer_allocate(player_buffer);
|
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 */
|
yet - don't open the audio device yet */
|
||||||
player_lock();
|
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;
|
pc.state = PLAYER_STATE_PLAY;
|
||||||
} else {
|
} else {
|
||||||
/* the audio device has failed - rollback to
|
player_open_output(player);
|
||||||
pause mode */
|
|
||||||
pc.error = PLAYER_ERROR_AUDIO;
|
|
||||||
|
|
||||||
player->paused = true;
|
|
||||||
|
|
||||||
player_lock();
|
player_lock();
|
||||||
}
|
}
|
||||||
@@ -558,8 +591,7 @@ static void player_process_command(struct player *player)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PLAYER_COMMAND_REFRESH:
|
case PLAYER_COMMAND_REFRESH:
|
||||||
if (audio_format_defined(&player->play_audio_format) &&
|
if (player->output_open && !player->paused) {
|
||||||
!player->paused) {
|
|
||||||
player_unlock();
|
player_unlock();
|
||||||
audio_output_all_check();
|
audio_output_all_check();
|
||||||
player_lock();
|
player_lock();
|
||||||
@@ -810,6 +842,7 @@ static void do_play(struct decoder_control *dc)
|
|||||||
.decoder_starting = false,
|
.decoder_starting = false,
|
||||||
.paused = false,
|
.paused = false,
|
||||||
.queued = true,
|
.queued = true,
|
||||||
|
.output_open = false,
|
||||||
.song = NULL,
|
.song = NULL,
|
||||||
.xfade = XFADE_UNKNOWN,
|
.xfade = XFADE_UNKNOWN,
|
||||||
.cross_fading = false,
|
.cross_fading = false,
|
||||||
@@ -833,6 +866,10 @@ static void do_play(struct decoder_control *dc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
player_lock();
|
player_lock();
|
||||||
|
|
||||||
|
if (pc.command == PLAYER_COMMAND_SEEK)
|
||||||
|
player.elapsed_time = pc.seek_where;
|
||||||
|
|
||||||
pc.state = PLAYER_STATE_PLAY;
|
pc.state = PLAYER_STATE_PLAY;
|
||||||
player_command_finished_locked();
|
player_command_finished_locked();
|
||||||
|
|
||||||
@@ -858,7 +895,7 @@ static void do_play(struct decoder_control *dc)
|
|||||||
/* not enough decoded buffer space yet */
|
/* not enough decoded buffer space yet */
|
||||||
|
|
||||||
if (!player.paused &&
|
if (!player.paused &&
|
||||||
audio_format_defined(&player.play_audio_format) &&
|
player.output_open &&
|
||||||
audio_output_all_check() < 4 &&
|
audio_output_all_check() < 4 &&
|
||||||
!player_send_silence(&player))
|
!player_send_silence(&player))
|
||||||
break;
|
break;
|
||||||
@@ -881,16 +918,6 @@ static void do_play(struct decoder_control *dc)
|
|||||||
if (!player_check_decoder_startup(&player))
|
if (!player_check_decoder_startup(&player))
|
||||||
break;
|
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();
|
player_lock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -973,7 +1000,7 @@ static void do_play(struct decoder_control *dc)
|
|||||||
audio_output_all_drain();
|
audio_output_all_drain();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (player.output_open) {
|
||||||
/* the decoder is too busy and hasn't provided
|
/* the decoder is too busy and hasn't provided
|
||||||
new PCM data in time: send silence (if the
|
new PCM data in time: send silence (if the
|
||||||
output pipe is empty) */
|
output pipe is empty) */
|
||||||
@@ -1021,6 +1048,7 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
|
|||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
switch (pc.command) {
|
switch (pc.command) {
|
||||||
|
case PLAYER_COMMAND_SEEK:
|
||||||
case PLAYER_COMMAND_QUEUE:
|
case PLAYER_COMMAND_QUEUE:
|
||||||
assert(pc.next_song != NULL);
|
assert(pc.next_song != NULL);
|
||||||
|
|
||||||
@@ -1034,7 +1062,6 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
|
|||||||
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
case PLAYER_COMMAND_SEEK:
|
|
||||||
case PLAYER_COMMAND_PAUSE:
|
case PLAYER_COMMAND_PAUSE:
|
||||||
pc.next_song = NULL;
|
pc.next_song = NULL;
|
||||||
player_command_finished_locked();
|
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)) {
|
if (g_path_is_absolute(uri)) {
|
||||||
/* XXX fs_charset vs utf8? */
|
/* XXX fs_charset vs utf8? */
|
||||||
char *prefix = base_uri != NULL
|
char *prefix = map_directory_fs(db_get_root());
|
||||||
? map_uri_fs(base_uri)
|
|
||||||
: map_directory_fs(db_get_root());
|
|
||||||
|
|
||||||
if (prefix == NULL || !g_str_has_prefix(uri, prefix) ||
|
if (prefix == NULL || !g_str_has_prefix(uri, prefix) ||
|
||||||
uri[strlen(prefix)] != '/') {
|
uri[strlen(prefix)] != '/') {
|
||||||
@@ -127,6 +125,7 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
base_uri = NULL;
|
||||||
uri += strlen(prefix) + 1;
|
uri += strlen(prefix) + 1;
|
||||||
g_free(prefix);
|
g_free(prefix);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include "socket_util.h"
|
#include "socket_util.h"
|
||||||
#include "fd_util.h"
|
#include "fd_util.h"
|
||||||
#include "glib_compat.h"
|
#include "glib_compat.h"
|
||||||
|
#include "glib_socket.h"
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.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 */
|
/* 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,
|
s->source_id = g_io_add_watch(channel, G_IO_IN,
|
||||||
server_socket_in_event, s);
|
server_socket_in_event, s);
|
||||||
g_io_channel_unref(channel);
|
g_io_channel_unref(channel);
|
||||||
@@ -252,7 +253,7 @@ server_socket_close(struct server_socket *ss)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
g_source_remove(s->source_id);
|
g_source_remove(s->source_id);
|
||||||
close(s->fd);
|
close_socket(s->fd);
|
||||||
s->fd = -1;
|
s->fd = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ socket_bind_listen(int domain, int type, int protocol,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_set_error(error, listen_quark(), errno,
|
g_set_error(error, listen_quark(), errno,
|
||||||
"setsockopt() failed: %s", g_strerror(errno));
|
"setsockopt() failed: %s", g_strerror(errno));
|
||||||
close(fd);
|
close_socket(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ socket_bind_listen(int domain, int type, int protocol,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_set_error(error, listen_quark(), errno,
|
g_set_error(error, listen_quark(), errno,
|
||||||
"%s", g_strerror(errno));
|
"%s", g_strerror(errno));
|
||||||
close(fd);
|
close_socket(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +138,7 @@ socket_bind_listen(int domain, int type, int protocol,
|
|||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
g_set_error(error, listen_quark(), errno,
|
g_set_error(error, listen_quark(), errno,
|
||||||
"listen() failed: %s", g_strerror(errno));
|
"listen() failed: %s", g_strerror(errno));
|
||||||
close(fd);
|
close_socket(fd);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user