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 |
.gitignoreMakefile.amNEWSconfigure.acmpd.service.in
src
client_new.c
decoder
decoder_api.cdecoder_control.cdecoder_control.hdecoder_internal.hdecoder_thread.cevent_pipe.cfd_util.cfd_util.hglib_socket.hinput
output
alsa_plugin.chttpd_client.chttpd_output_plugin.copenal_plugin.cpulse_output_plugin.csolaris_output_plugin.c
output_control.cpcm_convert.cpcm_format.cplayer_control.cplayer_thread.cplaylist_song.cserver_socket.csocket_util.curi.h
1
.gitignore
vendored
1
.gitignore
vendored
@@ -39,6 +39,7 @@ tags
|
||||
*~
|
||||
.#*
|
||||
.stgit*
|
||||
doc/doxygen.conf
|
||||
doc/protocol.html
|
||||
doc/protocol
|
||||
doc/user
|
||||
|
12
Makefile.am
12
Makefile.am
@@ -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
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
|
||||
|
49
configure.ac
49
configure.ac
@@ -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
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
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
|
||||
|
Reference in New Issue
Block a user