Compare commits
266 Commits
v0.16_alph
...
v0.16.8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
db1ce4eeeb | ||
![]() |
5acee73fc8 | ||
![]() |
466c337bcb | ||
![]() |
98a468a101 | ||
![]() |
47c58c01d1 | ||
![]() |
a9edf85a69 | ||
![]() |
e7a1862517 | ||
![]() |
d8e423df1a | ||
![]() |
09aa0dc676 | ||
![]() |
83174de420 | ||
![]() |
8ff0197a43 | ||
![]() |
de0f46b947 | ||
![]() |
79eb7623ef | ||
![]() |
b9e64d0472 | ||
![]() |
4f500149af | ||
![]() |
103832742d | ||
![]() |
3e7e0bcb18 | ||
![]() |
7d3d8f20ab | ||
![]() |
e1e3ce980a | ||
![]() |
7855a32579 | ||
![]() |
9c92afa5fe | ||
![]() |
66235fddff | ||
![]() |
f9c5d026f4 | ||
![]() |
48eb3ff8d9 | ||
![]() |
5646dcc791 | ||
![]() |
5d9876e338 | ||
![]() |
083340a937 | ||
![]() |
378fa5ee6a | ||
![]() |
4764daf3c2 | ||
![]() |
6357496d17 | ||
![]() |
001e2a604b | ||
![]() |
f370911c15 | ||
![]() |
39d52762d1 | ||
![]() |
8d45d0d104 | ||
![]() |
abd1949825 | ||
![]() |
4e6bc77a70 | ||
![]() |
531948358b | ||
![]() |
0d3ec9c324 | ||
![]() |
21caca4aea | ||
![]() |
fbf3edf07d | ||
![]() |
76fcf25898 | ||
![]() |
56257f072b | ||
![]() |
44401158e8 | ||
![]() |
97b4a6b51f | ||
![]() |
f405d27c56 | ||
![]() |
3a9697adf2 | ||
![]() |
96ad5b8444 | ||
![]() |
097e5dfbdc | ||
![]() |
2ef7ee6ca7 | ||
![]() |
2685b53b30 | ||
![]() |
533e4fcdad | ||
![]() |
f5d3859238 | ||
![]() |
ef39da5973 | ||
![]() |
81e8c4bbff | ||
![]() |
8ca3642429 | ||
![]() |
1dc000c06a | ||
![]() |
e1b032cbad | ||
![]() |
6f365c30eb | ||
![]() |
718e180423 | ||
![]() |
cead5e5bd7 | ||
![]() |
cf15629aea | ||
![]() |
a727d0bb0b | ||
![]() |
0a218ee56a | ||
![]() |
74beefcaf6 | ||
![]() |
399a3abefc | ||
![]() |
cee5036aca | ||
![]() |
790823abb4 | ||
![]() |
f546849352 | ||
![]() |
a85af593f1 | ||
![]() |
07067f8b95 | ||
![]() |
a1e824ada0 | ||
![]() |
f5f1bfbef1 | ||
![]() |
cd108ba3aa | ||
![]() |
2bb5bfa74e | ||
![]() |
624e7a447d | ||
![]() |
ef40e362c9 | ||
![]() |
6452461c39 | ||
![]() |
c30c46cd5f | ||
![]() |
d394017926 | ||
![]() |
04525c0259 | ||
![]() |
fa5e06f95d | ||
![]() |
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 | ||
![]() |
e888c9e827 | ||
![]() |
2556449b36 | ||
![]() |
446f9973cc | ||
![]() |
596f36bb78 | ||
![]() |
e7abdab58d | ||
![]() |
13cdc9a9f8 | ||
![]() |
a1b8806422 | ||
![]() |
e635d47912 | ||
![]() |
53ac72a878 | ||
![]() |
2be6184c8d | ||
![]() |
8b0b4ff086 | ||
![]() |
60f7ff3de5 | ||
![]() |
e76c752987 | ||
![]() |
042c1abc6e | ||
![]() |
3d12d7de62 | ||
![]() |
87593f95d4 | ||
![]() |
11626e48bf | ||
![]() |
b3df4dc2c9 | ||
![]() |
3db9ab82ea | ||
![]() |
2dc3acc5f0 | ||
![]() |
25686e5bce | ||
![]() |
8d70f808d9 | ||
![]() |
7c887af1ea | ||
![]() |
b7f435b50e | ||
![]() |
d3b15f8fda | ||
![]() |
838f7cd210 | ||
![]() |
13539961b2 | ||
![]() |
a26f2ef17d | ||
![]() |
d97c46bcdc | ||
![]() |
2b6542467c | ||
![]() |
8fa51faa38 | ||
![]() |
b2175629fd | ||
![]() |
2e28ed8f81 | ||
![]() |
4c4f8bf02a | ||
![]() |
e464be5f39 | ||
![]() |
d7d717f2ce | ||
![]() |
d1eeed6a5b | ||
![]() |
736fd0e293 | ||
![]() |
6592ca9f88 | ||
![]() |
762712c756 | ||
![]() |
73f9e17951 | ||
![]() |
7d6a605a85 | ||
![]() |
a6a8bdffc3 | ||
![]() |
296085ff23 | ||
![]() |
36aa8ce3c9 | ||
![]() |
c49c69d6ea | ||
![]() |
d5684f7444 | ||
![]() |
affb4bd923 | ||
![]() |
65772a74e0 | ||
![]() |
cca2c2f4ca | ||
![]() |
52e2fa91c4 | ||
![]() |
dca405a746 | ||
![]() |
3680a6bbbb | ||
![]() |
6aa6a9c272 | ||
![]() |
8d1c7ca206 | ||
![]() |
52b8e0f9ec | ||
![]() |
3c4f4793b5 | ||
![]() |
e2950a7e4d | ||
![]() |
4b4aa64261 | ||
![]() |
26735390ff | ||
![]() |
9402b23dd5 | ||
![]() |
246db3d565 | ||
![]() |
eaf414cbc8 | ||
![]() |
327d41c00f | ||
![]() |
05d8ce3bcd | ||
![]() |
def2fe8805 | ||
![]() |
f680b0a431 | ||
![]() |
d4b00ff11c | ||
![]() |
532f94a187 | ||
![]() |
87ad2f8542 | ||
![]() |
a8f891efcd | ||
![]() |
b5fc2419e8 | ||
![]() |
fe588a255b | ||
![]() |
1fc571088b | ||
![]() |
8d83914f05 | ||
![]() |
0fdcd381bc | ||
![]() |
4f293ecd6f | ||
![]() |
b6303313f0 | ||
![]() |
a28449a123 | ||
![]() |
6dcec36621 | ||
![]() |
84d0fd39a3 | ||
![]() |
4d4b7e3de0 | ||
![]() |
e2aea6bce5 | ||
![]() |
5779146a7f | ||
![]() |
a1d1c2beaa | ||
![]() |
ee9c60fad4 | ||
![]() |
1674a4ec82 | ||
![]() |
ce370bee60 | ||
![]() |
e257484870 | ||
![]() |
2a1f4539f6 | ||
![]() |
906efdd320 | ||
![]() |
948b8f35e6 | ||
![]() |
e776c605ad | ||
![]() |
8b2f4fc823 | ||
![]() |
03018611f8 | ||
![]() |
8f99c954ad | ||
![]() |
5735c9efc1 | ||
![]() |
e6c3acaa6f | ||
![]() |
44b4b50949 | ||
![]() |
9ad862d3a6 | ||
![]() |
77d71c4ee6 | ||
![]() |
8c0afd8557 | ||
![]() |
2a56300f7b | ||
![]() |
5f06999686 | ||
![]() |
4c09aeb5a1 | ||
![]() |
af892e7e80 | ||
![]() |
0022fb100b | ||
![]() |
4f2d67dfb0 | ||
![]() |
b5645ab29f | ||
![]() |
3149d1abf9 | ||
![]() |
59a417fc84 | ||
![]() |
b75d53413d | ||
![]() |
c44a744c0b | ||
![]() |
76cddfab90 | ||
![]() |
60b4f6b3eb | ||
![]() |
546232b1c0 | ||
![]() |
42c5788de3 | ||
![]() |
fb00e7fddc | ||
![]() |
41fdcf328c | ||
![]() |
144ad7992e | ||
![]() |
a0dd1a1b8b | ||
![]() |
c360e69162 | ||
![]() |
da01c6ef5b | ||
![]() |
fcd2355f4f | ||
![]() |
748a8a6f42 | ||
![]() |
cb9965bab5 | ||
![]() |
429ed24c99 | ||
![]() |
1ab46472ab | ||
![]() |
f6bbe1332f | ||
![]() |
11613347be | ||
![]() |
8f46f1520c | ||
![]() |
f2893b0d0f | ||
![]() |
c7265f9689 | ||
![]() |
46ab8d18e2 | ||
![]() |
f384f8da93 | ||
![]() |
23cd8a74be | ||
![]() |
cc1debc948 | ||
![]() |
5a3aa1262a | ||
![]() |
ad52eb236d | ||
![]() |
d2c2cbd0ae | ||
![]() |
462bba8e2f |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,11 +34,13 @@ missing
|
|||||||
mkinstalldirs
|
mkinstalldirs
|
||||||
mpd
|
mpd
|
||||||
mpd.exe
|
mpd.exe
|
||||||
|
mpd.service
|
||||||
stamp-h1
|
stamp-h1
|
||||||
tags
|
tags
|
||||||
*~
|
*~
|
||||||
.#*
|
.#*
|
||||||
.stgit*
|
.stgit*
|
||||||
|
doc/doxygen.conf
|
||||||
doc/protocol.html
|
doc/protocol.html
|
||||||
doc/protocol
|
doc/protocol
|
||||||
doc/user
|
doc/user
|
||||||
|
73
Makefile.am
73
Makefile.am
@@ -1,14 +1,17 @@
|
|||||||
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"'
|
||||||
|
|
||||||
bin_PROGRAMS = src/mpd
|
bin_PROGRAMS = src/mpd
|
||||||
|
|
||||||
|
noinst_LIBRARIES =
|
||||||
|
|
||||||
src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
|
src_mpd_CFLAGS = $(AM_CFLAGS) $(MPD_CFLAGS)
|
||||||
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
|
src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||||
|
$(AVAHI_CFLAGS) \
|
||||||
$(LIBWRAP_CFLAGS) \
|
$(LIBWRAP_CFLAGS) \
|
||||||
$(SQLITE_CFLAGS) \
|
$(SQLITE_CFLAGS) \
|
||||||
$(ARCHIVE_CFLAGS) \
|
$(ARCHIVE_CFLAGS) \
|
||||||
@@ -19,6 +22,7 @@ src_mpd_CPPFLAGS = $(AM_CPPFLAGS) \
|
|||||||
$(FILTER_CFLAGS) \
|
$(FILTER_CFLAGS) \
|
||||||
$(OUTPUT_CFLAGS)
|
$(OUTPUT_CFLAGS)
|
||||||
src_mpd_LDADD = $(MPD_LIBS) \
|
src_mpd_LDADD = $(MPD_LIBS) \
|
||||||
|
$(AVAHI_LIBS) \
|
||||||
$(LIBWRAP_LDFLAGS) \
|
$(LIBWRAP_LDFLAGS) \
|
||||||
$(SQLITE_LIBS) \
|
$(SQLITE_LIBS) \
|
||||||
$(ARCHIVE_LIBS) \
|
$(ARCHIVE_LIBS) \
|
||||||
@@ -34,6 +38,7 @@ mpd_headers = \
|
|||||||
src/check.h \
|
src/check.h \
|
||||||
src/notify.h \
|
src/notify.h \
|
||||||
src/ack.h \
|
src/ack.h \
|
||||||
|
src/ape.h \
|
||||||
src/audio.h \
|
src/audio.h \
|
||||||
src/audio_format.h \
|
src/audio_format.h \
|
||||||
src/audio_check.h \
|
src/audio_check.h \
|
||||||
@@ -80,7 +85,6 @@ mpd_headers = \
|
|||||||
src/encoder_api.h \
|
src/encoder_api.h \
|
||||||
src/exclude.h \
|
src/exclude.h \
|
||||||
src/fd_util.h \
|
src/fd_util.h \
|
||||||
src/fifo_buffer.h \
|
|
||||||
src/glib_compat.h \
|
src/glib_compat.h \
|
||||||
src/update.h \
|
src/update.h \
|
||||||
src/update_internal.h \
|
src/update_internal.h \
|
||||||
@@ -139,7 +143,6 @@ mpd_headers = \
|
|||||||
src/output/pulse_output_plugin.h \
|
src/output/pulse_output_plugin.h \
|
||||||
src/output/winmm_output_plugin.h \
|
src/output/winmm_output_plugin.h \
|
||||||
src/page.h \
|
src/page.h \
|
||||||
src/pcm_buffer.h \
|
|
||||||
src/pcm_utils.h \
|
src/pcm_utils.h \
|
||||||
src/pcm_convert.h \
|
src/pcm_convert.h \
|
||||||
src/pcm_volume.h \
|
src/pcm_volume.h \
|
||||||
@@ -186,6 +189,7 @@ mpd_headers = \
|
|||||||
src/refcount.h \
|
src/refcount.h \
|
||||||
src/replay_gain_config.h \
|
src/replay_gain_config.h \
|
||||||
src/replay_gain_info.h \
|
src/replay_gain_info.h \
|
||||||
|
src/replay_gain_ape.h \
|
||||||
src/sig_handlers.h \
|
src/sig_handlers.h \
|
||||||
src/song.h \
|
src/song.h \
|
||||||
src/song_print.h \
|
src/song_print.h \
|
||||||
@@ -237,6 +241,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 \
|
||||||
@@ -260,7 +265,8 @@ src_mpd_SOURCES = \
|
|||||||
src/dirvec.c \
|
src/dirvec.c \
|
||||||
src/exclude.c \
|
src/exclude.c \
|
||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
src/fifo_buffer.c \
|
src/fifo_buffer.c src/fifo_buffer.h \
|
||||||
|
src/growing_fifo.c src/growing_fifo.h \
|
||||||
src/filter_config.c \
|
src/filter_config.c \
|
||||||
src/filter_plugin.c \
|
src/filter_plugin.c \
|
||||||
src/filter_registry.c \
|
src/filter_registry.c \
|
||||||
@@ -293,6 +299,7 @@ src_mpd_SOURCES = \
|
|||||||
src/path.c \
|
src/path.c \
|
||||||
src/mapper.c \
|
src/mapper.c \
|
||||||
src/page.c \
|
src/page.c \
|
||||||
|
src/pcm_buffer.c src/pcm_buffer.h \
|
||||||
src/pcm_convert.c \
|
src/pcm_convert.c \
|
||||||
src/pcm_volume.c \
|
src/pcm_volume.c \
|
||||||
src/pcm_mix.c \
|
src/pcm_mix.c \
|
||||||
@@ -412,6 +419,8 @@ TAG_LIBS = \
|
|||||||
$(ID3TAG_LIBS)
|
$(ID3TAG_LIBS)
|
||||||
|
|
||||||
TAG_SRC = \
|
TAG_SRC = \
|
||||||
|
src/ape.c \
|
||||||
|
src/replay_gain_ape.c \
|
||||||
src/tag_ape.c
|
src/tag_ape.c
|
||||||
|
|
||||||
if HAVE_ID3TAG
|
if HAVE_ID3TAG
|
||||||
@@ -428,7 +437,6 @@ DECODER_CFLAGS = \
|
|||||||
$(SNDFILE_CFLAGS) \
|
$(SNDFILE_CFLAGS) \
|
||||||
$(AUDIOFILE_CFLAGS) \
|
$(AUDIOFILE_CFLAGS) \
|
||||||
$(LIBMIKMOD_CFLAGS) \
|
$(LIBMIKMOD_CFLAGS) \
|
||||||
$(MODPLUG_CFLAGS) \
|
|
||||||
$(GME_CFLAGS) \
|
$(GME_CFLAGS) \
|
||||||
$(SIDPLAY_CFLAGS) \
|
$(SIDPLAY_CFLAGS) \
|
||||||
$(FLUIDSYNTH_CFLAGS) \
|
$(FLUIDSYNTH_CFLAGS) \
|
||||||
@@ -444,7 +452,6 @@ DECODER_LIBS = \
|
|||||||
$(FLAC_LIBS) \
|
$(FLAC_LIBS) \
|
||||||
$(SNDFILE_LIBS) \
|
$(SNDFILE_LIBS) \
|
||||||
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
|
$(AUDIOFILE_LIBS) $(LIBMIKMOD_LIBS) \
|
||||||
$(MODPLUG_LIBS) \
|
|
||||||
$(GME_LIBS) \
|
$(GME_LIBS) \
|
||||||
$(SIDPLAY_LIBS) \
|
$(SIDPLAY_LIBS) \
|
||||||
$(FLUIDSYNTH_LIBS) \
|
$(FLUIDSYNTH_LIBS) \
|
||||||
@@ -517,7 +524,11 @@ DECODER_SRC += src/decoder/mikmod_decoder_plugin.c
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if HAVE_MODPLUG
|
if HAVE_MODPLUG
|
||||||
DECODER_SRC += src/decoder/modplug_decoder_plugin.c
|
libmodplug_decoder_plugin_a_SOURCES = src/decoder/modplug_decoder_plugin.c
|
||||||
|
libmodplug_decoder_plugin_a_CFLAGS = $(src_mpd_CFLAGS) $(MODPLUG_CFLAGS)
|
||||||
|
libmodplug_decoder_plugin_a_CPPFLAGS = $(src_mpd_CPPFLAGS)
|
||||||
|
noinst_LIBRARIES += libmodplug_decoder_plugin.a
|
||||||
|
DECODER_LIBS += libmodplug_decoder_plugin.a $(MODPLUG_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if ENABLE_SIDPLAY
|
if ENABLE_SIDPLAY
|
||||||
@@ -791,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
|
||||||
#
|
#
|
||||||
@@ -851,6 +871,7 @@ test_run_input_LDADD = $(MPD_LIBS) \
|
|||||||
$(INPUT_LIBS) \
|
$(INPUT_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_input_SOURCES = test/run_input.c \
|
test_run_input_SOURCES = test/run_input.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c \
|
src/conf.c src/tokenizer.c src/utils.c \
|
||||||
src/tag.c src/tag_pool.c src/tag_save.c \
|
src/tag.c src/tag_pool.c src/tag_save.c \
|
||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
@@ -898,6 +919,7 @@ test_run_decoder_LDADD = $(MPD_LIBS) \
|
|||||||
$(INPUT_LIBS) $(DECODER_LIBS) \
|
$(INPUT_LIBS) $(DECODER_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_decoder_SOURCES = test/run_decoder.c \
|
test_run_decoder_SOURCES = test/run_decoder.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c \
|
||||||
src/replay_gain_info.c \
|
src/replay_gain_info.c \
|
||||||
@@ -906,6 +928,7 @@ test_run_decoder_SOURCES = test/run_decoder.c \
|
|||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/timer.c \
|
src/timer.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
$(ARCHIVE_SRC) \
|
$(ARCHIVE_SRC) \
|
||||||
$(INPUT_SRC) \
|
$(INPUT_SRC) \
|
||||||
$(TAG_SRC) \
|
$(TAG_SRC) \
|
||||||
@@ -928,6 +951,7 @@ test_read_tags_SOURCES = test/read_tags.c \
|
|||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/timer.c \
|
src/timer.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
$(ARCHIVE_SRC) \
|
$(ARCHIVE_SRC) \
|
||||||
$(INPUT_SRC) \
|
$(INPUT_SRC) \
|
||||||
$(TAG_SRC) \
|
$(TAG_SRC) \
|
||||||
@@ -938,6 +962,7 @@ test_run_filter_LDADD = $(MPD_LIBS) \
|
|||||||
$(SAMPLERATE_LIBS) \
|
$(SAMPLERATE_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_filter_SOURCES = test/run_filter.c \
|
test_run_filter_SOURCES = test/run_filter.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/filter_plugin.c \
|
src/filter_plugin.c \
|
||||||
src/filter_registry.c \
|
src/filter_registry.c \
|
||||||
src/conf.c src/tokenizer.c src/utils.c \
|
src/conf.c src/tokenizer.c src/utils.c \
|
||||||
@@ -945,6 +970,7 @@ test_run_filter_SOURCES = test/run_filter.c \
|
|||||||
src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \
|
src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \
|
||||||
src/pcm_pack.c \
|
src/pcm_pack.c \
|
||||||
src/pcm_resample.c src/pcm_resample_fallback.c \
|
src/pcm_resample.c src/pcm_resample_fallback.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
@@ -960,19 +986,45 @@ endif
|
|||||||
if ENABLE_ENCODER
|
if ENABLE_ENCODER
|
||||||
noinst_PROGRAMS += test/run_encoder
|
noinst_PROGRAMS += test/run_encoder
|
||||||
test_run_encoder_SOURCES = test/run_encoder.c \
|
test_run_encoder_SOURCES = test/run_encoder.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c \
|
src/conf.c src/tokenizer.c \
|
||||||
src/utils.c \
|
src/utils.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
|
src/fifo_buffer.c src/growing_fifo.c \
|
||||||
$(ENCODER_SRC)
|
$(ENCODER_SRC)
|
||||||
|
test_run_encoder_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||||
|
$(ENCODER_CFLAGS)
|
||||||
test_run_encoder_LDADD = $(MPD_LIBS) \
|
test_run_encoder_LDADD = $(MPD_LIBS) \
|
||||||
$(ENCODER_LIBS) \
|
$(ENCODER_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if ENABLE_VORBIS_ENCODER
|
||||||
|
noinst_PROGRAMS += test/test_vorbis_encoder
|
||||||
|
test_test_vorbis_encoder_SOURCES = test/test_vorbis_encoder.c \
|
||||||
|
test/stdbin.h \
|
||||||
|
src/conf.c src/tokenizer.c \
|
||||||
|
src/utils.c \
|
||||||
|
src/tag.c src/tag_pool.c \
|
||||||
|
src/audio_check.c \
|
||||||
|
src/audio_format.c \
|
||||||
|
src/audio_parser.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
|
src/fifo_buffer.c src/growing_fifo.c \
|
||||||
|
$(ENCODER_SRC)
|
||||||
|
test_test_vorbis_encoder_CPPFLAGS = $(AM_CPPFLAGS) \
|
||||||
|
$(ENCODER_CFLAGS)
|
||||||
|
test_test_vorbis_encoder_LDADD = $(MPD_LIBS) \
|
||||||
|
$(ENCODER_LIBS) \
|
||||||
|
$(GLIB_LIBS)
|
||||||
|
endif
|
||||||
|
|
||||||
test_software_volume_SOURCES = test/software_volume.c \
|
test_software_volume_SOURCES = test/software_volume.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
src/pcm_volume.c
|
src/pcm_volume.c
|
||||||
@@ -980,6 +1032,7 @@ test_software_volume_LDADD = \
|
|||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
|
|
||||||
test_run_normalize_SOURCES = test/run_normalize.c \
|
test_run_normalize_SOURCES = test/run_normalize.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
src/AudioCompress/compress.c
|
src/AudioCompress/compress.c
|
||||||
@@ -991,6 +1044,7 @@ test_run_convert_SOURCES = test/run_convert.c \
|
|||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
src/pcm_channels.c \
|
src/pcm_channels.c \
|
||||||
src/pcm_format.c \
|
src/pcm_format.c \
|
||||||
src/pcm_pack.c \
|
src/pcm_pack.c \
|
||||||
@@ -1017,13 +1071,14 @@ test_run_output_LDADD = $(MPD_LIBS) \
|
|||||||
$(OUTPUT_LIBS) \
|
$(OUTPUT_LIBS) \
|
||||||
$(GLIB_LIBS)
|
$(GLIB_LIBS)
|
||||||
test_run_output_SOURCES = test/run_output.c \
|
test_run_output_SOURCES = test/run_output.c \
|
||||||
|
test/stdbin.h \
|
||||||
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
src/conf.c src/tokenizer.c src/utils.c src/log.c \
|
||||||
src/audio_check.c \
|
src/audio_check.c \
|
||||||
src/audio_format.c \
|
src/audio_format.c \
|
||||||
src/audio_parser.c \
|
src/audio_parser.c \
|
||||||
src/timer.c \
|
src/timer.c \
|
||||||
src/tag.c src/tag_pool.c \
|
src/tag.c src/tag_pool.c \
|
||||||
src/fifo_buffer.c \
|
src/fifo_buffer.c src/growing_fifo.c \
|
||||||
src/page.c \
|
src/page.c \
|
||||||
src/socket_util.c \
|
src/socket_util.c \
|
||||||
src/output_init.c src/output_list.c \
|
src/output_init.c src/output_list.c \
|
||||||
@@ -1040,6 +1095,7 @@ test_run_output_SOURCES = test/run_output.c \
|
|||||||
src/filter/normalize_filter_plugin.c \
|
src/filter/normalize_filter_plugin.c \
|
||||||
src/filter/volume_filter_plugin.c \
|
src/filter/volume_filter_plugin.c \
|
||||||
src/pcm_volume.c \
|
src/pcm_volume.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
src/AudioCompress/compress.c \
|
src/AudioCompress/compress.c \
|
||||||
src/replay_gain_info.c \
|
src/replay_gain_info.c \
|
||||||
src/replay_gain_config.c \
|
src/replay_gain_config.c \
|
||||||
@@ -1058,6 +1114,7 @@ test_read_mixer_SOURCES = test/read_mixer.c \
|
|||||||
src/filter_plugin.c \
|
src/filter_plugin.c \
|
||||||
src/filter/volume_filter_plugin.c \
|
src/filter/volume_filter_plugin.c \
|
||||||
src/fd_util.c \
|
src/fd_util.c \
|
||||||
|
src/pcm_buffer.c \
|
||||||
$(MIXER_SRC)
|
$(MIXER_SRC)
|
||||||
|
|
||||||
if ENABLE_BZIP2_TEST
|
if ENABLE_BZIP2_TEST
|
||||||
|
161
NEWS
161
NEWS
@@ -1,4 +1,137 @@
|
|||||||
ver 0.16 (20??/??/??)
|
ver 0.16.8 (2012/04/04)
|
||||||
|
* fix for libsamplerate assertion failure
|
||||||
|
* decoder:
|
||||||
|
- vorbis (and others): fix seeking at startup
|
||||||
|
- ffmpeg: read the "year" tag
|
||||||
|
* encoder:
|
||||||
|
- vorbis: generate end-of-stream packet before tag
|
||||||
|
- vorbis: generate end-of-stream packet when playback ends
|
||||||
|
* output:
|
||||||
|
- jack: check for connection failure before starting playback
|
||||||
|
- jack: workaround for libjack1 crash bug
|
||||||
|
- osx: fix stuttering due to buffering bug
|
||||||
|
* fix endless loop in text file reader
|
||||||
|
* update: skip symlinks in path that is to be updated
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.7 (2012/02/04)
|
||||||
|
* input:
|
||||||
|
- ffmpeg: support libavformat 0.7
|
||||||
|
* decoder:
|
||||||
|
- ffmpeg: support libavformat 0.8, libavcodec 0.9
|
||||||
|
- ffmpeg: support all MPD tags
|
||||||
|
* output:
|
||||||
|
- httpd: fix excessive buffering
|
||||||
|
- openal: force 16 bit playback, as 8 bit doesn't work
|
||||||
|
- osx: remove sleep call from render callback
|
||||||
|
- osx: clear render buffer when there's not enough data
|
||||||
|
* fix moving after current song
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.6 (2011/12/01)
|
||||||
|
* decoder:
|
||||||
|
- fix assertion failure when resuming streams
|
||||||
|
- ffmpeg: work around bogus channel count
|
||||||
|
* encoder:
|
||||||
|
- flac, null, wave: fix buffer corruption bug
|
||||||
|
- wave: support packed 24 bit samples
|
||||||
|
* mapper: fix the bogus "not a directory" error message
|
||||||
|
* mapper: check "x" and "r" permissions on music directory
|
||||||
|
* log: print reason for failure
|
||||||
|
* event_pipe: fix WIN32 regression
|
||||||
|
* define WINVER in ./configure
|
||||||
|
* WIN32: autodetect filesystem encoding
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.5 (2011/10/09)
|
||||||
|
* configure.ac
|
||||||
|
- disable assertions in the non-debugging build
|
||||||
|
- show solaris plugin result correctly
|
||||||
|
- add option --enable-solaris-output
|
||||||
|
* pcm_format: fix 32-to-24 bit conversion (the "silence" bug)
|
||||||
|
* input:
|
||||||
|
- rewind: reduce heap usage
|
||||||
|
* decoder:
|
||||||
|
- ffmpeg: higher precision timestamps
|
||||||
|
- ffmpeg: don't require key frame for seeking
|
||||||
|
- fix CUE track seeking
|
||||||
|
* output:
|
||||||
|
- openal: auto-fallback to mono if channel count is unsupported
|
||||||
|
* player:
|
||||||
|
- make seeking to CUE track more reliable
|
||||||
|
- the "seek" command works when MPD is stopped
|
||||||
|
- restore song position from state file (bug fix)
|
||||||
|
- fix crash that sometimes occurred when audio device fails on startup
|
||||||
|
- fix absolute path support in playlists
|
||||||
|
* WIN32: close sockets properly
|
||||||
|
* install systemd service file if systemd is available
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.4 (2011/09/01)
|
||||||
|
* don't abort configure when avahi is not found
|
||||||
|
* auto-detect libmad without pkg-config
|
||||||
|
* fix memory leaks
|
||||||
|
* don't resume playback when seeking to another song while paused
|
||||||
|
* apply follow_inside_symlinks to absolute symlinks
|
||||||
|
* fix playback discontinuation after seeking
|
||||||
|
* input:
|
||||||
|
- curl: limit the receive buffer size
|
||||||
|
- curl: implement a hard-coded timeout of 10 seconds
|
||||||
|
* decoder:
|
||||||
|
- ffmpeg: workaround for semantic API change in recent ffmpeg versions
|
||||||
|
- flac: validate the sample rate when scanning the tag
|
||||||
|
- wavpack: obey all decoder commands, stop at CUE track border
|
||||||
|
* encoder:
|
||||||
|
- vorbis: don't send end-of-stream on flush
|
||||||
|
* output:
|
||||||
|
- alsa: fix SIGFPE when alsa announces a period size of 0
|
||||||
|
- httpd: don't warn on client disconnect
|
||||||
|
- osx: don't drain the buffer when closing
|
||||||
|
- pulse: fix deadlock when resuming the stream
|
||||||
|
- pulse: fix deadlock when the stream was suspended
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.3 (2011/06/04)
|
||||||
|
* fix assertion failure in audio format mask parser
|
||||||
|
* fix NULL pointer dereference in playlist parser
|
||||||
|
* fix playlist files in base music directory
|
||||||
|
* database: allow directories with just playlists
|
||||||
|
* decoder:
|
||||||
|
- ffmpeg: support libavcodec 0.7
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.2 (2011/03/18)
|
||||||
|
* configure.ac:
|
||||||
|
- fix bashism in tremor test
|
||||||
|
* decoder:
|
||||||
|
- tremor: fix configure test
|
||||||
|
- gme: detect end of song
|
||||||
|
* encoder:
|
||||||
|
- vorbis: reset the Ogg stream after flush
|
||||||
|
* output:
|
||||||
|
- httpd: fix uninitialized variable
|
||||||
|
- httpd: include sys/socket.h
|
||||||
|
- oss: AFMT_S24_PACKED is little-endian
|
||||||
|
- oss: disable 24 bit playback on FreeBSD
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16.1 (2011/01/09)
|
||||||
|
* audio_check: fix parameter in prototype
|
||||||
|
* add void casts to suppress "result unused" warnings (clang)
|
||||||
|
* input:
|
||||||
|
- ffado: disable by default
|
||||||
|
* decoder:
|
||||||
|
- mad: work around build failure on Solaris
|
||||||
|
- resolve modplug vs. libsndfile cflags/headers conflict
|
||||||
|
* output:
|
||||||
|
- solaris: add missing parameter to open_cloexec() cal
|
||||||
|
- osx: fix up audio format first, then apply it to device
|
||||||
|
* player_thread: discard empty chunks while cross-fading
|
||||||
|
* player_thread: fix assertion failure due to early seek
|
||||||
|
* output_thread: fix double lock
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.16 (2010/12/11)
|
||||||
* protocol:
|
* protocol:
|
||||||
- send song modification time to client
|
- send song modification time to client
|
||||||
- added "update" idle event
|
- added "update" idle event
|
||||||
@@ -20,7 +153,9 @@ ver 0.16 (20??/??/??)
|
|||||||
* tags:
|
* tags:
|
||||||
- added tags "ArtistSort", "AlbumArtistSort"
|
- added tags "ArtistSort", "AlbumArtistSort"
|
||||||
- id3: revised "performer" tag support
|
- id3: revised "performer" tag support
|
||||||
|
- id3: support multiple values
|
||||||
- ape: MusicBrainz tags
|
- ape: MusicBrainz tags
|
||||||
|
- ape: support multiple values
|
||||||
* decoders:
|
* decoders:
|
||||||
- don't try a plugin twice (MIME type & suffix)
|
- don't try a plugin twice (MIME type & suffix)
|
||||||
- don't fall back to "mad" unless no plugin matches
|
- don't fall back to "mad" unless no plugin matches
|
||||||
@@ -88,6 +223,7 @@ ver 0.16 (20??/??/??)
|
|||||||
- fall back to track gain if album gain is unavailable
|
- fall back to track gain if album gain is unavailable
|
||||||
- optionally use hardware mixer to apply replay gain
|
- optionally use hardware mixer to apply replay gain
|
||||||
- added mode "auto"
|
- added mode "auto"
|
||||||
|
- parse replay gain from APE tags
|
||||||
* log unused/unknown block parameters
|
* log unused/unknown block parameters
|
||||||
* removed the deprecated "error_file" option
|
* removed the deprecated "error_file" option
|
||||||
* save state when stopped
|
* save state when stopped
|
||||||
@@ -109,6 +245,29 @@ ver 0.16 (20??/??/??)
|
|||||||
* make single mode 'sticky'
|
* make single mode 'sticky'
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.17 (2011/??/??)
|
||||||
|
* encoder:
|
||||||
|
- vorbis: reset the Ogg stream after flush
|
||||||
|
* decoders:
|
||||||
|
- vorbis: fix tremor support
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.16 (2011/03/13)
|
||||||
|
* output:
|
||||||
|
- ao: initialize the ao_sample_format struct
|
||||||
|
- jack: fix crash with mono playback
|
||||||
|
* encoders:
|
||||||
|
- lame: explicitly configure the output sample rate
|
||||||
|
* update: log all file permission problems
|
||||||
|
|
||||||
|
|
||||||
|
ver 0.15.15 (2010/11/08)
|
||||||
|
* input:
|
||||||
|
- rewind: fix assertion failure
|
||||||
|
* output:
|
||||||
|
- shout: artist comes first in stream title
|
||||||
|
|
||||||
|
|
||||||
ver 0.15.14 (2010/11/06)
|
ver 0.15.14 (2010/11/06)
|
||||||
* player_thread: fix assertion failure due to wrong music pipe on seek
|
* player_thread: fix assertion failure due to wrong music pipe on seek
|
||||||
* output_thread: fix assertion failure due to race condition in OPEN
|
* output_thread: fix assertion failure due to race condition in OPEN
|
||||||
|
226
configure.ac
226
configure.ac
@@ -1,5 +1,5 @@
|
|||||||
AC_PREREQ(2.60)
|
AC_PREREQ(2.60)
|
||||||
AC_INIT(mpd, 0.16~alpha3, musicpd-dev-team@lists.sourceforge.net)
|
AC_INIT(mpd, 0.16.8, 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)
|
||||||
@@ -13,6 +13,7 @@ dnl Programs
|
|||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
AC_PROG_CC_C99
|
AC_PROG_CC_C99
|
||||||
AC_PROG_CXX
|
AC_PROG_CXX
|
||||||
|
AC_PROG_RANLIB
|
||||||
|
|
||||||
HAVE_CXX=yes
|
HAVE_CXX=yes
|
||||||
if test x$CXX = xg++; then
|
if test x$CXX = xg++; then
|
||||||
@@ -32,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)
|
||||||
@@ -50,6 +68,7 @@ AC_CANONICAL_HOST
|
|||||||
|
|
||||||
case "$host_os" in
|
case "$host_os" in
|
||||||
mingw32* | windows*)
|
mingw32* | windows*)
|
||||||
|
AM_CPPFLAGS="$AM_CPPFLAGS -DWINVER=0x0501"
|
||||||
MPD_LIBS="$MPD_LIBS -lws2_32"
|
MPD_LIBS="$MPD_LIBS -lws2_32"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@@ -157,7 +176,7 @@ AC_ARG_ENABLE(documentation,
|
|||||||
|
|
||||||
AC_ARG_ENABLE(ffado,
|
AC_ARG_ENABLE(ffado,
|
||||||
AS_HELP_STRING([--enable-ffado], [enable libffado (FireWire) support]),,
|
AS_HELP_STRING([--enable-ffado], [enable libffado (FireWire) support]),,
|
||||||
[enable_ffado=auto])
|
[enable_ffado=no])
|
||||||
|
|
||||||
AC_ARG_ENABLE(ffmpeg,
|
AC_ARG_ENABLE(ffmpeg,
|
||||||
AS_HELP_STRING([--enable-ffmpeg],
|
AS_HELP_STRING([--enable-ffmpeg],
|
||||||
@@ -170,9 +189,9 @@ AC_ARG_ENABLE(fifo,
|
|||||||
enable_fifo=yes)
|
enable_fifo=yes)
|
||||||
|
|
||||||
AC_ARG_ENABLE(flac,
|
AC_ARG_ENABLE(flac,
|
||||||
AS_HELP_STRING([--disable-flac],
|
AS_HELP_STRING([--enable-flac],
|
||||||
[disable flac support (default: enable)]),,
|
[enable FLAC decoder]),,
|
||||||
enable_flac=yes)
|
enable_flac=auto)
|
||||||
|
|
||||||
AC_ARG_ENABLE(fluidsynth,
|
AC_ARG_ENABLE(fluidsynth,
|
||||||
AS_HELP_STRING([--enable-fluidsynth],
|
AS_HELP_STRING([--enable-fluidsynth],
|
||||||
@@ -195,9 +214,9 @@ AC_ARG_ENABLE(httpd-output,
|
|||||||
[enable_httpd_output=auto])
|
[enable_httpd_output=auto])
|
||||||
|
|
||||||
AC_ARG_ENABLE(id3,
|
AC_ARG_ENABLE(id3,
|
||||||
AS_HELP_STRING([--disable-id3],
|
AS_HELP_STRING([--enable-id3],
|
||||||
[disable id3 support (default: enable)]),,
|
[enable id3 support]),,
|
||||||
enable_id3=yes)
|
enable_id3=auto)
|
||||||
|
|
||||||
AC_ARG_ENABLE(inotify,
|
AC_ARG_ENABLE(inotify,
|
||||||
AS_HELP_STRING([--disable-inotify],
|
AS_HELP_STRING([--disable-inotify],
|
||||||
@@ -321,6 +340,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]),,
|
||||||
@@ -352,9 +376,9 @@ AC_ARG_ENABLE(un,
|
|||||||
[enable_un=yes])
|
[enable_un=yes])
|
||||||
|
|
||||||
AC_ARG_ENABLE(vorbis,
|
AC_ARG_ENABLE(vorbis,
|
||||||
AS_HELP_STRING([--disable-vorbis],
|
AS_HELP_STRING([--enable-vorbis],
|
||||||
[disable Ogg Vorbis support (default: enable)]),,
|
[enable Ogg Vorbis decoder]),,
|
||||||
enable_vorbis=yes)
|
enable_vorbis=auto)
|
||||||
|
|
||||||
AC_ARG_ENABLE(vorbis-encoder,
|
AC_ARG_ENABLE(vorbis-encoder,
|
||||||
AS_HELP_STRING([--enable-vorbis-encoder],
|
AS_HELP_STRING([--enable-vorbis-encoder],
|
||||||
@@ -408,6 +432,11 @@ dnl ---------------------------------------------------------------------------
|
|||||||
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12 gthread-2.0],,
|
PKG_CHECK_MODULES([GLIB], [glib-2.0 >= 2.12 gthread-2.0],,
|
||||||
[AC_MSG_ERROR([GLib 2.12 is required])])
|
[AC_MSG_ERROR([GLib 2.12 is required])])
|
||||||
|
|
||||||
|
if test x$GCC = xyes; then
|
||||||
|
# suppress warnings in the GLib headers
|
||||||
|
GLIB_CFLAGS=`echo $GLIB_CFLAGS |sed -e 's,-I/,-isystem /,g'`
|
||||||
|
fi
|
||||||
|
|
||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
dnl Protocol Options
|
dnl Protocol Options
|
||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
@@ -422,7 +451,11 @@ if test x$enable_ipv6 = xyes; then
|
|||||||
AC_EGREP_CPP([AP_maGiC_VALUE],
|
AC_EGREP_CPP([AP_maGiC_VALUE],
|
||||||
[
|
[
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#else
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#endif
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#ifdef PF_INET6
|
#ifdef PF_INET6
|
||||||
#ifdef AF_INET6
|
#ifdef AF_INET6
|
||||||
@@ -509,13 +542,8 @@ fi
|
|||||||
AM_CONDITIONAL(HAVE_CUE, test x$enable_cue = xyes)
|
AM_CONDITIONAL(HAVE_CUE, test x$enable_cue = xyes)
|
||||||
|
|
||||||
dnl -------------------------------- libid3tag --------------------------------
|
dnl -------------------------------- libid3tag --------------------------------
|
||||||
if test x$enable_id3 = xyes; then
|
MPD_AUTO_PKG_LIB(id3, ID3TAG, id3tag, id3tag, id3_file_open, [-lid3tag -lz], [],
|
||||||
PKG_CHECK_MODULES([ID3TAG], [id3tag],,
|
[id3tag], [libid3tag not found])
|
||||||
AC_CHECK_LIB(id3tag, id3_file_open,
|
|
||||||
[ID3TAG_LIBS="-lid3tag -lz" ID3TAG_CFLAGS=""],
|
|
||||||
enable_id3=no))
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test x$enable_id3 = xyes; then
|
if test x$enable_id3 = xyes; then
|
||||||
AC_DEFINE(HAVE_ID3TAG, 1, [Define to use id3tag])
|
AC_DEFINE(HAVE_ID3TAG, 1, [Define to use id3tag])
|
||||||
fi
|
fi
|
||||||
@@ -529,36 +557,39 @@ dnl ---------------------------------------------------------------------------
|
|||||||
dnl --------------------------------- zeroconf --------------------------------
|
dnl --------------------------------- zeroconf --------------------------------
|
||||||
|
|
||||||
case $with_zeroconf in
|
case $with_zeroconf in
|
||||||
no|avahi|bonjour)
|
no|bonjour)
|
||||||
|
enable_avahi=no
|
||||||
;;
|
;;
|
||||||
|
|
||||||
|
avahi)
|
||||||
|
enable_avahi=yes
|
||||||
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
with_zeroconf=auto
|
with_zeroconf=auto
|
||||||
|
enable_avahi=auto
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if test x$with_zeroconf != xno; then
|
MPD_AUTO_PKG(avahi, AVAHI, [avahi-client avahi-glib],
|
||||||
if test x$with_zeroconf = xavahi || test x$with_zeroconf = xauto; then
|
[avahi client library], [avahi client+glib not found])
|
||||||
PKG_CHECK_MODULES([AVAHI], [avahi-client avahi-glib],
|
if test x$enable_avahi = xyes; then
|
||||||
[found_avahi=1;AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])]
|
AC_DEFINE([HAVE_AVAHI], 1, [Define to enable Avahi Zeroconf support])
|
||||||
MPD_LIBS="$MPD_LIBS $AVAHI_LIBS" MPD_CFLAGS="$MPD_CFLAGS $AVAHI_CFLAGS",
|
|
||||||
[found_avahi=0])
|
|
||||||
fi
|
|
||||||
|
|
||||||
if test x$found_avahi = x1; then
|
|
||||||
with_zeroconf=avahi
|
with_zeroconf=avahi
|
||||||
elif test x$with_zeroconf = xavahi; then
|
fi
|
||||||
AC_MSG_ERROR([Avahi support requested but not found])
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
AM_CONDITIONAL(HAVE_AVAHI, test x$enable_avahi = xyes)
|
||||||
|
|
||||||
|
enable_bounjour=no
|
||||||
|
if test x$with_zeroconf != xno; then
|
||||||
if test x$with_zeroconf = xbonjour || test x$with_zeroconf = xauto; then
|
if test x$with_zeroconf = xbonjour || test x$with_zeroconf = xauto; then
|
||||||
AC_CHECK_HEADER(dns_sd.h,
|
AC_CHECK_HEADER(dns_sd.h,
|
||||||
[found_bonjour=1;AC_DEFINE([HAVE_BONJOUR], 1, [Define to enable Bonjour Zeroconf support])],
|
[enable_bonjour=yes;AC_DEFINE([HAVE_BONJOUR], 1, [Define to enable Bonjour Zeroconf support])])
|
||||||
[found_bonjour=0])
|
|
||||||
AC_CHECK_LIB(dns_sd, DNSServiceRegister,
|
AC_CHECK_LIB(dns_sd, DNSServiceRegister,
|
||||||
MPD_LIBS="$MPD_LIBS -ldns_sd")
|
MPD_LIBS="$MPD_LIBS -ldns_sd")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test x$found_bonjour = x1; then
|
if test x$enable_bonjour = xyes; then
|
||||||
with_zeroconf=bonjour
|
with_zeroconf=bonjour
|
||||||
elif test x$with_zeroconf = xbonjour; then
|
elif test x$with_zeroconf = xbonjour; then
|
||||||
AC_MSG_ERROR([Bonjour support requested but not found])
|
AC_MSG_ERROR([Bonjour support requested but not found])
|
||||||
@@ -573,7 +604,6 @@ if test x$with_zeroconf != xno; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(HAVE_ZEROCONF, test x$with_zeroconf != xno)
|
AM_CONDITIONAL(HAVE_ZEROCONF, test x$with_zeroconf != xno)
|
||||||
AM_CONDITIONAL(HAVE_AVAHI, test x$with_zeroconf = xavahi)
|
|
||||||
AM_CONDITIONAL(HAVE_BONJOUR, test x$with_zeroconf = xbonjour)
|
AM_CONDITIONAL(HAVE_BONJOUR, test x$with_zeroconf = xbonjour)
|
||||||
|
|
||||||
dnl ---------------------------------------------------------------------------
|
dnl ---------------------------------------------------------------------------
|
||||||
@@ -633,11 +663,6 @@ if test x$enable_lastfm = xyes; then
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
|
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
|
||||||
|
|
||||||
dnl ---------------------------------- libogg ---------------------------------
|
|
||||||
if test x$with_tremor == xno || test -z $with_tremor; then
|
|
||||||
PKG_CHECK_MODULES(OGG, [ogg], enable_ogg=yes, enable_ogg=no)
|
|
||||||
fi
|
|
||||||
|
|
||||||
dnl ---------------------------------- libmms ---------------------------------
|
dnl ---------------------------------- libmms ---------------------------------
|
||||||
MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4],
|
MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4],
|
||||||
[libmms mms:// protocol support], [libmms not found])
|
[libmms mms:// protocol support], [libmms not found])
|
||||||
@@ -752,10 +777,12 @@ fi
|
|||||||
AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
|
AM_CONDITIONAL(HAVE_FFMPEG, test x$enable_ffmpeg = xyes)
|
||||||
|
|
||||||
dnl ----------------------------------- FLAC ----------------------------------
|
dnl ----------------------------------- FLAC ----------------------------------
|
||||||
|
|
||||||
|
MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1],
|
||||||
|
[FLAC decoder], [libFLAC not found])
|
||||||
|
|
||||||
if test x$enable_flac = xyes; then
|
if test x$enable_flac = xyes; then
|
||||||
PKG_CHECK_MODULES(FLAC, [flac >= 1.1],
|
AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support])
|
||||||
AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support]),
|
|
||||||
enable_flac=no)
|
|
||||||
|
|
||||||
oldcflags="$CFLAGS"
|
oldcflags="$CFLAGS"
|
||||||
oldlibs="$LIBS"
|
oldlibs="$LIBS"
|
||||||
@@ -770,12 +797,10 @@ if test x$enable_flac = xyes; then
|
|||||||
LIBS="$oldlibs"
|
LIBS="$oldlibs"
|
||||||
|
|
||||||
if test x$enable_oggflac = xflac; then
|
if test x$enable_oggflac = xflac; then
|
||||||
if test x$enable_ogg = xyes; then
|
PKG_CHECK_MODULES(OGG, [ogg],
|
||||||
FLAC_LIBS="${FLAC_LIBS} -logg"
|
[FLAC_LIBS="${FLAC_LIBS} ${OGG_LIBS}" FLAC_CFLAGS="${FLAC_CFLAGS} ${OGG_CFLAGS}"],
|
||||||
else
|
[enable_oggflac=yes;
|
||||||
enable_oggflac=yes
|
AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.")])
|
||||||
AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.")
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -801,7 +826,8 @@ if test x$enable_gme = xyes; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
dnl ---------------------------------- libmad ---------------------------------
|
dnl ---------------------------------- libmad ---------------------------------
|
||||||
MPD_AUTO_PKG(mad, MAD, [mad],
|
MPD_AUTO_PKG_LIB(mad, MAD, [mad],
|
||||||
|
mad, mad_stream_init, [-lmad], [],
|
||||||
[libmad MP3 decoder plugin], [libmad not found])
|
[libmad MP3 decoder plugin], [libmad not found])
|
||||||
if test x$enable_mad = xyes; then
|
if test x$enable_mad = xyes; then
|
||||||
AC_DEFINE(HAVE_MAD, 1, [Define to use libmad])
|
AC_DEFINE(HAVE_MAD, 1, [Define to use libmad])
|
||||||
@@ -849,15 +875,6 @@ if test x$enable_modplug = xyes; then
|
|||||||
fi
|
fi
|
||||||
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
|
AM_CONDITIONAL(HAVE_MODPLUG, test x$enable_modplug = xyes)
|
||||||
|
|
||||||
dnl --------------------------- sndfile/modplug test --------------------------
|
|
||||||
if test x$enable_sndfile = xauto && test x$enable_modplug = xyes; then
|
|
||||||
dnl If modplug is enabled, enable sndfile only if explicitly
|
|
||||||
dnl requested - modplug's modplug/sndfile.h is known to
|
|
||||||
dnl conflict with libsndfile's sndfile.h.
|
|
||||||
AC_MSG_NOTICE([disabling libsndfile auto-detection, because the modplug decoder is enabled])
|
|
||||||
enable_sndfile=no
|
|
||||||
fi
|
|
||||||
|
|
||||||
dnl -------------------------------- libsndfile -------------------------------
|
dnl -------------------------------- libsndfile -------------------------------
|
||||||
dnl See above test, which may disable this.
|
dnl See above test, which may disable this.
|
||||||
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
|
MPD_AUTO_PKG(sndfile, SNDFILE, [sndfile],
|
||||||
@@ -942,13 +959,19 @@ if test x$enable_tremor = xyes; then
|
|||||||
ac_save_LIBS="$LIBS"
|
ac_save_LIBS="$LIBS"
|
||||||
CFLAGS="$CFLAGS $TREMOR_CFLAGS"
|
CFLAGS="$CFLAGS $TREMOR_CFLAGS"
|
||||||
LIBS="$LIBS $TREMOR_LIBS"
|
LIBS="$LIBS $TREMOR_LIBS"
|
||||||
AC_CHECK_LIB(vorbisidec,ov_read,enable_vorbis=yes,enable_vorbis=no;
|
AC_CHECK_LIB(vorbisidec,ov_read,,enable_tremor=no;
|
||||||
AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support]))
|
AC_MSG_WARN([vorbisidec lib needed for ogg support with tremor -- disabling ogg support]))
|
||||||
CFLAGS="$ac_save_CFLAGS"
|
CFLAGS="$ac_save_CFLAGS"
|
||||||
LIBS="$ac_save_LIBS"
|
LIBS="$ac_save_LIBS"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test x$enable_tremor = xyes; then
|
||||||
AC_DEFINE(HAVE_TREMOR,1,
|
AC_DEFINE(HAVE_TREMOR,1,
|
||||||
[Define to use tremor (libvorbisidec) for ogg support])
|
[Define to use tremor (libvorbisidec) for ogg support])
|
||||||
|
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
|
||||||
|
else
|
||||||
|
TREMOR_CFLAGS=
|
||||||
|
TREMOR_LIBS=
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AC_SUBST(TREMOR_CFLAGS)
|
AC_SUBST(TREMOR_CFLAGS)
|
||||||
@@ -974,21 +997,21 @@ fi
|
|||||||
AM_CONDITIONAL(HAVE_OGGFLAC, test x$enable_oggflac = xyes)
|
AM_CONDITIONAL(HAVE_OGGFLAC, test x$enable_oggflac = xyes)
|
||||||
|
|
||||||
dnl -------------------------------- Ogg Vorbis -------------------------------
|
dnl -------------------------------- Ogg Vorbis -------------------------------
|
||||||
if test x$enable_vorbis = xyes; then
|
|
||||||
if test x$enable_tremor = xyes; then
|
if test x$enable_tremor = xyes; then
|
||||||
|
if test x$enable_vorbis = xyes; then
|
||||||
AC_MSG_WARN(["OggTremor detected, could not enable Vorbis."])
|
AC_MSG_WARN(["OggTremor detected, could not enable Vorbis."])
|
||||||
enable_vorbis=no
|
|
||||||
elif test x$enable_ogg = xyes; then
|
|
||||||
PKG_CHECK_MODULES(VORBIS, [vorbis vorbisfile],
|
|
||||||
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support]),
|
|
||||||
enable_vorbis=no)
|
|
||||||
else
|
|
||||||
AC_MSG_WARN(["Ogg not detected, could not enable Vorbis."])
|
|
||||||
enable_vorbis=no
|
|
||||||
fi
|
fi
|
||||||
|
enable_vorbis=no
|
||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes)
|
MPD_AUTO_PKG(vorbis, VORBIS, [vorbis vorbisfile ogg],
|
||||||
|
[Ogg Vorbis decoder], [libvorbis not found])
|
||||||
|
if test x$enable_vorbis = xyes; then
|
||||||
|
AC_DEFINE(ENABLE_VORBIS_DECODER, 1, [Define for Ogg Vorbis support])
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL(ENABLE_VORBIS_DECODER, test x$enable_vorbis = xyes || test x$enable_tremor = xyes)
|
||||||
|
|
||||||
dnl --------------------------------- sidplay ---------------------------------
|
dnl --------------------------------- sidplay ---------------------------------
|
||||||
found_sidplay=$HAVE_CXX
|
found_sidplay=$HAVE_CXX
|
||||||
@@ -1083,7 +1106,7 @@ if
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
AM_CONDITIONAL(HAVE_OGG_COMMON,
|
AM_CONDITIONAL(HAVE_OGG_COMMON,
|
||||||
test x$enable_vorbis = xyes || test x$enable_oggflac = xyes || test x$enable_flac = xyes)
|
test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_oggflac = xyes || test x$enable_flac = xyes)
|
||||||
|
|
||||||
AM_CONDITIONAL(HAVE_FLAC_COMMON,
|
AM_CONDITIONAL(HAVE_FLAC_COMMON,
|
||||||
test x$enable_flac = xyes || test x$enable_oggflac = xyes)
|
test x$enable_flac = xyes || test x$enable_oggflac = xyes)
|
||||||
@@ -1377,16 +1400,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)
|
||||||
|
|
||||||
@@ -1458,17 +1487,15 @@ 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
|
||||||
then
|
then
|
||||||
MPD_CHECK_FLAG([-Wall])
|
MPD_CHECK_FLAG([-Wall])
|
||||||
MPD_CHECK_FLAG([-Wextra])
|
MPD_CHECK_FLAG([-Wextra])
|
||||||
MPD_CHECK_FLAG([-Wno-deprecated-declarations])
|
|
||||||
MPD_CHECK_FLAG([-Wmissing-prototypes])
|
MPD_CHECK_FLAG([-Wmissing-prototypes])
|
||||||
MPD_CHECK_FLAG([-Wshadow])
|
MPD_CHECK_FLAG([-Wshadow])
|
||||||
MPD_CHECK_FLAG([-Wpointer-arith])
|
MPD_CHECK_FLAG([-Wpointer-arith])
|
||||||
@@ -1487,6 +1514,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 ---------------------------------------------------------------------------
|
||||||
@@ -1495,23 +1523,23 @@ dnl ---------------------------------------------------------------------------
|
|||||||
echo ''
|
echo ''
|
||||||
echo '########### MPD CONFIGURATION ############'
|
echo '########### MPD CONFIGURATION ############'
|
||||||
|
|
||||||
echo -ne '\nArchive support:\n\t'
|
printf '\nArchive support:\n\t'
|
||||||
results(bzip2,[bzip2])
|
results(bzip2,[bzip2])
|
||||||
results(iso9660,[ISO9660])
|
results(iso9660,[ISO9660])
|
||||||
results(zzip,[ZIP])
|
results(zzip,[ZIP])
|
||||||
|
|
||||||
if test x$with_zeroconf != xno; then
|
if test x$with_zeroconf != xno; then
|
||||||
echo -ne '\nAutodiscovery support:\n\t'
|
printf '\nAutodiscovery support:\n\t'
|
||||||
results(avahi, [Avahi])
|
results(avahi, [Avahi])
|
||||||
results(bonjour, [Bonjour])
|
results(bonjour, [Bonjour])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -ne '\nClient support:\n\t'
|
printf '\nClient support:\n\t'
|
||||||
results(ipv6, "IPv6")
|
results(ipv6, "IPv6")
|
||||||
results(tcp, "TCP")
|
results(tcp, "TCP")
|
||||||
results(un,[UNIX Domain Sockets])
|
results(un,[UNIX Domain Sockets])
|
||||||
|
|
||||||
echo -ne '\nFile format support:\n\t'
|
printf '\nFile format support:\n\t'
|
||||||
results(aac, [AAC])
|
results(aac, [AAC])
|
||||||
results(sidplay, [C64 SID])
|
results(sidplay, [C64 SID])
|
||||||
results(ffmpeg, [FFMPEG])
|
results(ffmpeg, [FFMPEG])
|
||||||
@@ -1519,7 +1547,7 @@ results(flac, [FLAC])
|
|||||||
results(fluidsynth, [FluidSynth])
|
results(fluidsynth, [FluidSynth])
|
||||||
results(gme, [GME])
|
results(gme, [GME])
|
||||||
results(sndfile, [libsndfile])
|
results(sndfile, [libsndfile])
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(mikmod, [MikMod])
|
results(mikmod, [MikMod])
|
||||||
results(modplug, [MODPLUG])
|
results(modplug, [MODPLUG])
|
||||||
results(mad, [MAD])
|
results(mad, [MAD])
|
||||||
@@ -1527,23 +1555,23 @@ results(mpg123, [MPG123])
|
|||||||
results(mp4, [MP4])
|
results(mp4, [MP4])
|
||||||
results(mpc, [Musepack])
|
results(mpc, [Musepack])
|
||||||
results(oggflac, [OggFLAC], flac)
|
results(oggflac, [OggFLAC], flac)
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(tremor, [OggTremor])
|
results(tremor, [OggTremor])
|
||||||
results(vorbis, [OggVorbis])
|
results(vorbis, [OggVorbis])
|
||||||
results(audiofile, [WAVE])
|
results(audiofile, [WAVE])
|
||||||
results(wavpack, [WavPack])
|
results(wavpack, [WavPack])
|
||||||
results(wildmidi, [WildMidi])
|
results(wildmidi, [WildMidi])
|
||||||
|
|
||||||
echo -en '\nOther features:\n\t'
|
printf '\nOther features:\n\t'
|
||||||
results(lsr, [libsamplerate])
|
results(lsr, [libsamplerate])
|
||||||
results(inotify, [inotify])
|
results(inotify, [inotify])
|
||||||
results(sqlite, [SQLite])
|
results(sqlite, [SQLite])
|
||||||
|
|
||||||
echo -en '\nMetadata support:\n\t'
|
printf '\nMetadata support:\n\t'
|
||||||
results(cue,[cue])
|
results(cue,[cue])
|
||||||
results(id3,[ID3])
|
results(id3,[ID3])
|
||||||
|
|
||||||
echo -en '\nPlayback support:\n\t'
|
printf '\nPlayback support:\n\t'
|
||||||
results(alsa,ALSA)
|
results(alsa,ALSA)
|
||||||
results(ffado,FFADO)
|
results(ffado,FFADO)
|
||||||
results(fifo,FIFO)
|
results(fifo,FIFO)
|
||||||
@@ -1552,22 +1580,22 @@ results(httpd_output,[HTTP Daemon])
|
|||||||
results(jack,[JACK])
|
results(jack,[JACK])
|
||||||
results(ao,[libao])
|
results(ao,[libao])
|
||||||
results(oss,[OSS])
|
results(oss,[OSS])
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(openal,[OpenAL])
|
results(openal,[OpenAL])
|
||||||
results(osx, [OS X])
|
results(osx, [OS X])
|
||||||
results(pipe_output, [Pipeline])
|
results(pipe_output, [Pipeline])
|
||||||
results(pulse, [PulseAudio])
|
results(pulse, [PulseAudio])
|
||||||
results(mvp, [Media MVP])
|
results(mvp, [Media MVP])
|
||||||
results(shout, [SHOUTcast])
|
results(shout, [SHOUTcast])
|
||||||
echo -ne '\n\t'
|
printf '\n\t'
|
||||||
results(solaris, [Solaris])
|
results(solaris_output, [Solaris])
|
||||||
results(winmm_output, [WinMM])
|
results(winmm_output, [WinMM])
|
||||||
|
|
||||||
if
|
if
|
||||||
test x$enable_shout = xyes ||
|
test x$enable_shout = xyes ||
|
||||||
test x$enable_recorder = xyes ||
|
test x$enable_recorder = xyes ||
|
||||||
test x$enable_httpd_output = xyes; then
|
test x$enable_httpd_output = xyes; then
|
||||||
echo -en '\nStreaming encoder support:\n\t'
|
printf '\nStreaming encoder support:\n\t'
|
||||||
results(flac_encoder, [FLAC])
|
results(flac_encoder, [FLAC])
|
||||||
results(lame_encoder, [LAME])
|
results(lame_encoder, [LAME])
|
||||||
results(vorbis_encoder, [Ogg Vorbis])
|
results(vorbis_encoder, [Ogg Vorbis])
|
||||||
@@ -1575,23 +1603,19 @@ if
|
|||||||
results(wave_encoder, [WAVE])
|
results(wave_encoder, [WAVE])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -en '\nStreaming support:\n\t'
|
printf '\nStreaming support:\n\t'
|
||||||
results(curl,[CURL])
|
results(curl,[CURL])
|
||||||
results(lastfm,[Last.FM])
|
results(lastfm,[Last.FM])
|
||||||
results(mms,[MMS])
|
results(mms,[MMS])
|
||||||
|
|
||||||
echo -ne '\n\n##########################################\n\n'
|
printf '\n\n##########################################\n\n'
|
||||||
|
|
||||||
if test x$enable_sndfile = xyes && test x$enable_modplug = xyes; then
|
echo 'Generating files needed for compilation'
|
||||||
AC_MSG_WARN([compilation may fail, because libmodplug conflicts with libsndfile])
|
|
||||||
AC_MSG_WARN([libmodplug ships modplug/sndfile.h, which hides libsndfile's sndfile.h])
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -ne 'Generating files needed for compilation\n'
|
|
||||||
|
|
||||||
dnl ---------------------------------------------------------------------------
|
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.'
|
||||||
|
@@ -57,7 +57,7 @@
|
|||||||
Some example code:
|
Some example code:
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<programlisting lang="C">static inline bool
|
<programlisting lang="C">static inline int
|
||||||
foo(const char *abc, int xyz)
|
foo(const char *abc, int xyz)
|
||||||
{
|
{
|
||||||
if (abc == NULL) {
|
if (abc == NULL) {
|
||||||
|
134
doc/protocol.xml
134
doc/protocol.xml
@@ -8,18 +8,54 @@
|
|||||||
<title>General protocol syntax</title>
|
<title>General protocol syntax</title>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Requests</title>
|
<title>Protocol overview</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If arguments contain spaces, they should be surrounded by double quotation
|
The MPD command protocol exchanges line-based text records
|
||||||
marks.
|
between client and server over TCP. Once the client is
|
||||||
|
connected to the server, they conduct a conversation until the
|
||||||
|
client closes the connection. The conversation flow is always
|
||||||
|
initiated by the client.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The client transmits a command sequence, terminated by the
|
||||||
|
newline character <constant>\n</constant>. The server will
|
||||||
|
respond with one or more lines, the last of which will be a
|
||||||
|
completion code.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
When the client connects to the server, the server will answer
|
||||||
|
with the following line:
|
||||||
|
|
||||||
|
<synopsis>OK MPD version</synopsis>
|
||||||
|
|
||||||
|
where <varname>version</varname> is a version identifier such as
|
||||||
|
0.12.2. This version identifier is the version of the protocol
|
||||||
|
spoken, not the real version of the daemon. (There is no way to
|
||||||
|
retrieve this real version identifier from the connection.)
|
||||||
|
</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title>Requests</title>
|
||||||
|
|
||||||
<cmdsynopsis>
|
<cmdsynopsis>
|
||||||
<command>COMMAND</command>
|
<command>COMMAND</command>
|
||||||
<arg rep="repeat"><replaceable>ARG</replaceable></arg>
|
<arg rep="repeat"><replaceable>ARG</replaceable></arg>
|
||||||
</cmdsynopsis>
|
</cmdsynopsis>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
If arguments contain spaces, they should be surrounded by double
|
||||||
|
quotation marks.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
Argument strings are separated from the command and any other
|
||||||
|
arguments by linear white-space (' ' or '\t').
|
||||||
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
All data between the client and the server is encoded in
|
All data between the client and the server is encoded in
|
||||||
UTF-8. (Note: In UTF-8 all standard ansi characters, 0-127 are
|
UTF-8. (Note: In UTF-8 all standard ansi characters, 0-127 are
|
||||||
@@ -38,13 +74,97 @@
|
|||||||
<title>Responses</title>
|
<title>Responses</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
A command returns <returnvalue>OK</returnvalue> on completion
|
A command returns <returnvalue>OK</returnvalue> on completion or
|
||||||
or <returnvalue>ACK some error</returnvalue> on failure.
|
<returnvalue>ACK some error</returnvalue> on failure. These
|
||||||
These denote the end of command execution.
|
denote the end of command execution.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
<title>Failure responses</title>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The nature of the error can be gleaned from the information
|
||||||
|
that follows the <returnvalue>ACK</returnvalue>.
|
||||||
|
<returnvalue>ACK</returnvalue> lines are of the form:
|
||||||
|
|
||||||
|
<synopsis>ACK [error@command_listNum] {current_command} message_text\n</synopsis>
|
||||||
|
|
||||||
|
These responses are generated by a call to
|
||||||
|
<function>commandError</function>. They contain four separate
|
||||||
|
terms. Let's look at each of them:
|
||||||
|
|
||||||
|
<itemizedlist>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<returnvalue>error</returnvalue>: numeric value of one
|
||||||
|
of the <constant>ACK_ERROR</constant> constants defined
|
||||||
|
in <filename>ack.h</filename>.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<returnvalue>command_listNum</returnvalue>:
|
||||||
|
offset of the command that caused the error in a <link
|
||||||
|
linkend="command_lists">Command List</link>.
|
||||||
|
An error will always cause a command list to terminate
|
||||||
|
at the command that causes the error.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<returnvalue>current_command</returnvalue>:
|
||||||
|
name of the command, in a <link
|
||||||
|
linkend="command_lists">Command List</link>,
|
||||||
|
that was executing when the error occurred.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
<returnvalue>message_text</returnvalue>:
|
||||||
|
some (hopefully) informative text that describes the
|
||||||
|
nature of the error.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</itemizedlist>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<example>
|
||||||
|
<title>foo</title>
|
||||||
|
<para>
|
||||||
|
An example might help. Consider the following sequence
|
||||||
|
sent from the client to the server.
|
||||||
|
|
||||||
|
<synopsis>
|
||||||
|
command_list_begin
|
||||||
|
volume 86
|
||||||
|
play 10240
|
||||||
|
status
|
||||||
|
command_list_end
|
||||||
|
</synopsis>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
The server responds with:
|
||||||
|
|
||||||
|
<returnvalue>
|
||||||
|
ACK [50@1] {play} song doesn't exist: "10240"
|
||||||
|
</returnvalue>
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
This tells us that the play command, which was the
|
||||||
|
second in the list (the first or only command is
|
||||||
|
numbered 0), failed with error 50. The number 50
|
||||||
|
translates to <constant>ACK_ERROR_NO_EXIST</constant>--the
|
||||||
|
song doesn't exist. This is reiterated by the message text
|
||||||
|
which also tells us which song doesn't exist.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
</example>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="command_lists">
|
||||||
<title>Command lists</title>
|
<title>Command lists</title>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
|
@@ -446,7 +446,7 @@ cd mpd-version</programlisting>
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To configure a filter, add a
|
To configure a playlist plugin, add a
|
||||||
<varname>playlist_plugin</varname> block to
|
<varname>playlist_plugin</varname> block to
|
||||||
<filename>mpd.conf</filename>:
|
<filename>mpd.conf</filename>:
|
||||||
</para>
|
</para>
|
||||||
|
12
m4/faad.m4
12
m4/faad.m4
@@ -58,7 +58,7 @@ if test x$enable_aac = xyes; then
|
|||||||
fi
|
fi
|
||||||
if test x$enable_aac = xyes; then
|
if test x$enable_aac = xyes; then
|
||||||
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
|
AC_MSG_CHECKING(that FAAD2 uses buffer and bufferlen)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -82,9 +82,9 @@ int main() {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
|
])],[AC_MSG_RESULT(yes);AC_DEFINE(HAVE_FAAD_BUFLEN_FUNCS,1,[Define if FAAD2 uses buflen in function calls])],[AC_MSG_RESULT(no);
|
||||||
AC_MSG_CHECKING(that FAAD2 can even be used)
|
AC_MSG_CHECKING(that FAAD2 can even be used)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
@@ -113,7 +113,7 @@ int main() {
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
|
])],AC_MSG_RESULT(yes),[AC_MSG_RESULT(no);enable_aac=no])
|
||||||
])
|
])
|
||||||
fi
|
fi
|
||||||
if test x$enable_aac = xyes; then
|
if test x$enable_aac = xyes; then
|
||||||
@@ -136,7 +136,7 @@ if test x$enable_aac = xyes; then
|
|||||||
CPPFLAGS=$CFLAGS
|
CPPFLAGS=$CFLAGS
|
||||||
|
|
||||||
AC_MSG_CHECKING(for broken libfaad headers)
|
AC_MSG_CHECKING(for broken libfaad headers)
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
#include <faad.h>
|
#include <faad.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -148,7 +148,7 @@ int main() {
|
|||||||
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
faacDecInit2(NULL, NULL, 0, &sample_rate, &channels);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
],
|
])],
|
||||||
[AC_MSG_RESULT(correct)],
|
[AC_MSG_RESULT(correct)],
|
||||||
[AC_MSG_RESULT(broken);
|
[AC_MSG_RESULT(broken);
|
||||||
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
|
AC_DEFINE(HAVE_FAAD_LONG, 1, [Define if faad.h uses the broken "unsigned long" pointers])])
|
||||||
|
@@ -63,3 +63,18 @@ AC_DEFUN([MPD_AUTO_PKG], [
|
|||||||
|
|
||||||
MPD_AUTO_RESULT([$1], [$4], [$5])
|
MPD_AUTO_RESULT([$1], [$4], [$5])
|
||||||
])
|
])
|
||||||
|
|
||||||
|
dnl Check with pkg-config first, fall back to AC_CHECK_LIB.
|
||||||
|
dnl
|
||||||
|
dnl Parameters: varname1, varname2, pkgname, libname, symname, libs, cflags, description, errmsg
|
||||||
|
AC_DEFUN([MPD_AUTO_PKG_LIB], [
|
||||||
|
if eval "test x`echo '$'enable_$1` != xno"; then
|
||||||
|
PKG_CHECK_MODULES([$2], [$3],
|
||||||
|
[eval "found_$1=yes"],
|
||||||
|
AC_CHECK_LIB($4, $5,
|
||||||
|
[eval "found_$1=yes $2_LIBS='$6' $2_CFLAGS='$7'"],
|
||||||
|
[eval "found_$1=no"]))
|
||||||
|
fi
|
||||||
|
|
||||||
|
MPD_AUTO_RESULT([$1], [$8], [$9])
|
||||||
|
])
|
||||||
|
@@ -4,9 +4,9 @@ AC_DEFUN([MPD_CHECK_FLAG],[
|
|||||||
[mpd_check_cflag_$var],[
|
[mpd_check_cflag_$var],[
|
||||||
save_CFLAGS="$CFLAGS"
|
save_CFLAGS="$CFLAGS"
|
||||||
CFLAGS="$CFLAGS $1"
|
CFLAGS="$CFLAGS $1"
|
||||||
AC_COMPILE_IFELSE([
|
AC_COMPILE_IFELSE([AC_LANG_SOURCE([
|
||||||
int main(void) { return 0; }
|
int main(void) { return 0; }
|
||||||
], [ eval "mpd_check_cflag_$var=yes"
|
])], [ eval "mpd_check_cflag_$var=yes"
|
||||||
], [ eval "mpd_check_cflag_$var=no" ])
|
], [ eval "mpd_check_cflag_$var=no" ])
|
||||||
CFLAGS="$save_CFLAGS"
|
CFLAGS="$save_CFLAGS"
|
||||||
])
|
])
|
||||||
|
@@ -1,19 +1,19 @@
|
|||||||
AC_DEFUN([results], [
|
AC_DEFUN([results], [
|
||||||
dnl This is a hack to allow "with" names, otherwise "enable".
|
dnl This is a hack to allow "with" names, otherwise "enable".
|
||||||
num=`expr match $1 'with'`
|
num=`expr $1 : 'with'`
|
||||||
if test "$num" != "0"; then
|
if test "$num" != "0"; then
|
||||||
var="`echo '$'$1`"
|
var="`echo '$'$1`"
|
||||||
else
|
else
|
||||||
var="`echo '$'enable_$1`"
|
var="`echo '$'enable_$1`"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo -n '('
|
printf '('
|
||||||
if eval "test x$var = xyes"; then
|
if eval "test x$var = xyes"; then
|
||||||
echo -n '+'
|
printf '+'
|
||||||
elif test -n "$3" && eval "test x$var = x$3"; then
|
elif test -n "$3" && eval "test x$var = x$3"; then
|
||||||
echo -n '+'
|
printf '+'
|
||||||
else
|
else
|
||||||
echo -n '-'
|
printf '-'
|
||||||
fi
|
fi
|
||||||
echo -n "$2) "
|
printf '%s) ' "$2"
|
||||||
])
|
])
|
||||||
|
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
|
@@ -3,7 +3,7 @@ PWD=`pwd`
|
|||||||
|
|
||||||
## If we're not in the scripts directory
|
## If we're not in the scripts directory
|
||||||
## assume the base directory.
|
## assume the base directory.
|
||||||
if test "`basename $PWD`" == "scripts"; then
|
if test "`basename $PWD`" = "scripts"; then
|
||||||
cd ../
|
cd ../
|
||||||
else
|
else
|
||||||
MYOLDPWD=`pwd`
|
MYOLDPWD=`pwd`
|
||||||
@@ -18,7 +18,7 @@ fi
|
|||||||
make
|
make
|
||||||
make dist
|
make dist
|
||||||
|
|
||||||
if test "`basename $PWD`" == "scripts"; then
|
if test "`basename $PWD`" = "scripts"; then
|
||||||
cd contrib/
|
cd contrib/
|
||||||
else
|
else
|
||||||
cd $MYOLDPWD
|
cd $MYOLDPWD
|
||||||
|
115
src/ape.c
Normal file
115
src/ape.c
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "ape.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
struct ape_footer {
|
||||||
|
unsigned char id[8];
|
||||||
|
uint32_t version;
|
||||||
|
uint32_t length;
|
||||||
|
uint32_t count;
|
||||||
|
unsigned char flags[4];
|
||||||
|
unsigned char reserved[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
ape_scan_internal(FILE *fp, tag_ape_callback_t callback, void *ctx)
|
||||||
|
{
|
||||||
|
/* determine if file has an apeV2 tag */
|
||||||
|
struct ape_footer footer;
|
||||||
|
if (fseek(fp, -(long)sizeof(footer), SEEK_END) ||
|
||||||
|
fread(&footer, 1, sizeof(footer), fp) != sizeof(footer) ||
|
||||||
|
memcmp(footer.id, "APETAGEX", sizeof(footer.id)) != 0 ||
|
||||||
|
GUINT32_FROM_LE(footer.version) != 2000)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* find beginning of ape tag */
|
||||||
|
size_t remaining = GUINT32_FROM_LE(footer.length);
|
||||||
|
if (remaining <= sizeof(footer) + 10 ||
|
||||||
|
/* refuse to load more than one megabyte of tag data */
|
||||||
|
remaining > 1024 * 1024 ||
|
||||||
|
fseek(fp, -(long)remaining, SEEK_END))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* read tag into buffer */
|
||||||
|
remaining -= sizeof(footer);
|
||||||
|
assert(remaining > 10);
|
||||||
|
|
||||||
|
char *buffer = g_malloc(remaining);
|
||||||
|
if (fread(buffer, 1, remaining, fp) != remaining) {
|
||||||
|
g_free(buffer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* read tags */
|
||||||
|
unsigned n = GUINT32_FROM_LE(footer.count);
|
||||||
|
const char *p = buffer;
|
||||||
|
while (n-- && remaining > 10) {
|
||||||
|
size_t size = GUINT32_FROM_LE(*(const uint32_t *)p);
|
||||||
|
p += 4;
|
||||||
|
remaining -= 4;
|
||||||
|
unsigned long flags = GUINT32_FROM_LE(*(const uint32_t *)p);
|
||||||
|
p += 4;
|
||||||
|
remaining -= 4;
|
||||||
|
|
||||||
|
/* get the key */
|
||||||
|
const char *key = p;
|
||||||
|
while (remaining > size && *p != '\0') {
|
||||||
|
p++;
|
||||||
|
remaining--;
|
||||||
|
}
|
||||||
|
p++;
|
||||||
|
remaining--;
|
||||||
|
|
||||||
|
/* get the value */
|
||||||
|
if (remaining < size)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!callback(flags, key, p, size, ctx))
|
||||||
|
break;
|
||||||
|
|
||||||
|
p += size;
|
||||||
|
remaining -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_free(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
|
||||||
|
fp = fopen(path_fs, "rb");
|
||||||
|
if (fp == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool success = ape_scan_internal(fp, callback, ctx);
|
||||||
|
fclose(fp);
|
||||||
|
return success;
|
||||||
|
}
|
42
src/ape.h
Normal file
42
src/ape.h
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_APE_H
|
||||||
|
#define MPD_APE_H
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef bool (*tag_ape_callback_t)(unsigned long flags, const char *key,
|
||||||
|
const char *value, size_t value_length,
|
||||||
|
void *ctx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Scans the APE tag values from a file.
|
||||||
|
*
|
||||||
|
* @param path_fs the path of the file in filesystem encoding
|
||||||
|
* @return false if the file could not be opened or if no APE tag is
|
||||||
|
* present
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
tag_ape_scan(const char *path_fs, tag_ape_callback_t callback, void *ctx);
|
||||||
|
|
||||||
|
#endif
|
@@ -38,7 +38,7 @@ bool
|
|||||||
audio_check_sample_rate(unsigned long sample_rate, GError **error_r);
|
audio_check_sample_rate(unsigned long sample_rate, GError **error_r);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
audio_check_sample_format(unsigned sample_format, GError **error_r);
|
audio_check_sample_format(enum sample_format, GError **error_r);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
audio_check_channel_count(unsigned sample_format, GError **error_r);
|
audio_check_channel_count(unsigned sample_format, GError **error_r);
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
enum sample_format {
|
enum sample_format {
|
||||||
SAMPLE_FORMAT_UNDEFINED = 0,
|
SAMPLE_FORMAT_UNDEFINED = 0,
|
||||||
@@ -219,6 +220,9 @@ static inline void
|
|||||||
audio_format_mask_apply(struct audio_format *af,
|
audio_format_mask_apply(struct audio_format *af,
|
||||||
const struct audio_format *mask)
|
const struct audio_format *mask)
|
||||||
{
|
{
|
||||||
|
assert(audio_format_valid(af));
|
||||||
|
assert(audio_format_mask_valid(mask));
|
||||||
|
|
||||||
if (mask->sample_rate != 0)
|
if (mask->sample_rate != 0)
|
||||||
af->sample_rate = mask->sample_rate;
|
af->sample_rate = mask->sample_rate;
|
||||||
|
|
||||||
@@ -227,6 +231,8 @@ audio_format_mask_apply(struct audio_format *af,
|
|||||||
|
|
||||||
if (mask->channels != 0)
|
if (mask->channels != 0)
|
||||||
af->channels = mask->channels;
|
af->channels = mask->channels;
|
||||||
|
|
||||||
|
assert(audio_format_valid(af));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -192,6 +192,8 @@ audio_format_parse(struct audio_format *dest, const char *src,
|
|||||||
}
|
}
|
||||||
|
|
||||||
audio_format_init(dest, rate, sample_format, channels);
|
audio_format_init(dest, rate, sample_format, channels);
|
||||||
|
assert(mask ? audio_format_mask_valid(dest)
|
||||||
|
: audio_format_valid(dest));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -194,8 +194,6 @@ parse_cmdline(int argc, char **argv, struct options *options,
|
|||||||
if(g_file_test(system_path,
|
if(g_file_test(system_path,
|
||||||
G_FILE_TEST_IS_REGULAR)) {
|
G_FILE_TEST_IS_REGULAR)) {
|
||||||
ret = config_read_file(system_path,error_r);
|
ret = config_read_file(system_path,error_r);
|
||||||
g_free(system_path);
|
|
||||||
g_free(&system_config_dirs);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++i;;
|
++i;;
|
||||||
|
@@ -464,7 +464,7 @@ handle_currentsong(struct client *client,
|
|||||||
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
|
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
|
||||||
{
|
{
|
||||||
playlist_print_current(client, &g_playlist);
|
playlist_print_current(client, &g_playlist);
|
||||||
return PLAYLIST_RESULT_SUCCESS;
|
return COMMAND_RETURN_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum command_return
|
static enum command_return
|
||||||
@@ -749,7 +749,7 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
|
|||||||
|
|
||||||
result = playlist_open_into_queue(argv[1], &g_playlist);
|
result = playlist_open_into_queue(argv[1], &g_playlist);
|
||||||
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
|
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
|
||||||
return result;
|
return print_playlist_result(client, result);
|
||||||
|
|
||||||
result = playlist_load_spl(&g_playlist, argv[1]);
|
result = playlist_load_spl(&g_playlist, argv[1]);
|
||||||
return print_playlist_result(client, result);
|
return print_playlist_result(client, result);
|
||||||
@@ -1715,14 +1715,10 @@ handle_sticker_song(struct client *client, int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
sticker = sticker_song_get(song);
|
sticker = sticker_song_get(song);
|
||||||
if (NULL == sticker) {
|
if (sticker) {
|
||||||
command_error(client, ACK_ERROR_NO_EXIST,
|
|
||||||
"no stickers found");
|
|
||||||
return COMMAND_RETURN_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
sticker_print(client, sticker);
|
sticker_print(client, sticker);
|
||||||
sticker_free(sticker);
|
sticker_free(sticker);
|
||||||
|
}
|
||||||
|
|
||||||
return COMMAND_RETURN_OK;
|
return COMMAND_RETURN_OK;
|
||||||
/* set song song_id id key */
|
/* set song song_id id key */
|
||||||
|
11
src/conf.c
11
src/conf.c
@@ -367,6 +367,7 @@ config_read_file(const char *file, GError **error_r)
|
|||||||
assert(*line != 0);
|
assert(*line != 0);
|
||||||
g_propagate_prefixed_error(error_r, error,
|
g_propagate_prefixed_error(error_r, error,
|
||||||
"line %i: ", count);
|
"line %i: ", count);
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,6 +379,7 @@ config_read_file(const char *file, GError **error_r)
|
|||||||
g_set_error(error_r, config_quark(), 0,
|
g_set_error(error_r, config_quark(), 0,
|
||||||
"unrecognized parameter in config file at "
|
"unrecognized parameter in config file at "
|
||||||
"line %i: %s\n", count, name);
|
"line %i: %s\n", count, name);
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,6 +389,7 @@ config_read_file(const char *file, GError **error_r)
|
|||||||
"config parameter \"%s\" is first defined "
|
"config parameter \"%s\" is first defined "
|
||||||
"on line %i and redefined on line %i\n",
|
"on line %i and redefined on line %i\n",
|
||||||
name, param->line, count);
|
name, param->line, count);
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,6 +401,7 @@ config_read_file(const char *file, GError **error_r)
|
|||||||
if (*line != '{') {
|
if (*line != '{') {
|
||||||
g_set_error(error_r, config_quark(), 0,
|
g_set_error(error_r, config_quark(), 0,
|
||||||
"line %i: '{' expected", count);
|
"line %i: '{' expected", count);
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,12 +410,15 @@ config_read_file(const char *file, GError **error_r)
|
|||||||
g_set_error(error_r, config_quark(), 0,
|
g_set_error(error_r, config_quark(), 0,
|
||||||
"line %i: Unknown tokens after '{'",
|
"line %i: Unknown tokens after '{'",
|
||||||
count);
|
count);
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
param = config_read_block(fp, &count, string, error_r);
|
param = config_read_block(fp, &count, string, error_r);
|
||||||
if (param == NULL)
|
if (param == NULL) {
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* a string value */
|
/* a string value */
|
||||||
|
|
||||||
@@ -428,6 +435,7 @@ config_read_file(const char *file, GError **error_r)
|
|||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -435,6 +443,7 @@ config_read_file(const char *file, GError **error_r)
|
|||||||
g_set_error(error_r, config_quark(), 0,
|
g_set_error(error_r, config_quark(), 0,
|
||||||
"line %i: Unknown tokens after value",
|
"line %i: Unknown tokens after value",
|
||||||
count);
|
count);
|
||||||
|
fclose(fp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -180,7 +180,7 @@ db_check(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Check if we can write to the directory */
|
/* Check if we can write to the directory */
|
||||||
if (access(dirPath, R_OK | W_OK)) {
|
if (access(dirPath, X_OK | W_OK)) {
|
||||||
g_warning("Can't create db file in \"%s\": %s",
|
g_warning("Can't create db file in \"%s\": %s",
|
||||||
dirPath, strerror(errno));
|
dirPath, strerror(errno));
|
||||||
g_free(dirPath);
|
g_free(dirPath);
|
||||||
|
@@ -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"
|
||||||
@@ -63,14 +64,14 @@ audiofile_file_read(AFvirtualfile *vfile, void *data, size_t length)
|
|||||||
return nbytes;
|
return nbytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static AFfileoffset
|
||||||
audiofile_file_length(AFvirtualfile *vfile)
|
audiofile_file_length(AFvirtualfile *vfile)
|
||||||
{
|
{
|
||||||
struct input_stream *is = (struct input_stream *) vfile->closure;
|
struct input_stream *is = (struct input_stream *) vfile->closure;
|
||||||
return is->size;
|
return is->size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static AFfileoffset
|
||||||
audiofile_file_tell(AFvirtualfile *vfile)
|
audiofile_file_tell(AFvirtualfile *vfile)
|
||||||
{
|
{
|
||||||
struct input_stream *is = (struct input_stream *) vfile->closure;
|
struct input_stream *is = (struct input_stream *) vfile->closure;
|
||||||
@@ -85,8 +86,8 @@ audiofile_file_destroy(AFvirtualfile *vfile)
|
|||||||
vfile->closure = NULL;
|
vfile->closure = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long
|
static AFfileoffset
|
||||||
audiofile_file_seek(AFvirtualfile *vfile, long offset, int is_relative)
|
audiofile_file_seek(AFvirtualfile *vfile, AFfileoffset offset, int is_relative)
|
||||||
{
|
{
|
||||||
struct input_stream *is = (struct input_stream *) vfile->closure;
|
struct input_stream *is = (struct input_stream *) vfile->closure;
|
||||||
int whence = (is_relative ? SEEK_CUR : SEEK_SET);
|
int whence = (is_relative ? SEEK_CUR : SEEK_SET);
|
||||||
|
@@ -40,7 +40,12 @@
|
|||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavformat/avio.h>
|
#include <libavformat/avio.h>
|
||||||
|
#include <libavutil/avutil.h>
|
||||||
#include <libavutil/log.h>
|
#include <libavutil/log.h>
|
||||||
|
#include <libavutil/mathematics.h>
|
||||||
|
#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(51,5,0)
|
||||||
|
#include <libavutil/dict.h>
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
@@ -81,11 +86,19 @@ mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
|
|||||||
|
|
||||||
#endif /* !OLD_FFMPEG_INCLUDES */
|
#endif /* !OLD_FFMPEG_INCLUDES */
|
||||||
|
|
||||||
|
#ifndef AV_VERSION_INT
|
||||||
|
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mpd_ffmpeg_stream {
|
struct mpd_ffmpeg_stream {
|
||||||
struct decoder *decoder;
|
struct decoder *decoder;
|
||||||
struct input_stream *input;
|
struct input_stream *input;
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
|
||||||
|
AVIOContext *io;
|
||||||
|
#else
|
||||||
ByteIOContext *io;
|
ByteIOContext *io;
|
||||||
|
#endif
|
||||||
unsigned char buffer[8192];
|
unsigned char buffer[8192];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -102,13 +115,11 @@ static int64_t
|
|||||||
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
|
mpd_ffmpeg_stream_seek(void *opaque, int64_t pos, int whence)
|
||||||
{
|
{
|
||||||
struct mpd_ffmpeg_stream *stream = opaque;
|
struct mpd_ffmpeg_stream *stream = opaque;
|
||||||
bool ret;
|
|
||||||
|
|
||||||
if (whence == AVSEEK_SIZE)
|
if (whence == AVSEEK_SIZE)
|
||||||
return stream->input->size;
|
return stream->input->size;
|
||||||
|
|
||||||
ret = input_stream_seek(stream->input, pos, whence, NULL);
|
if (!input_stream_seek(stream->input, pos, whence, NULL))
|
||||||
if (!ret)
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
return stream->input->offset;
|
return stream->input->offset;
|
||||||
@@ -120,11 +131,19 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
|
|||||||
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
|
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
|
||||||
stream->decoder = decoder;
|
stream->decoder = decoder;
|
||||||
stream->input = input;
|
stream->input = input;
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
|
||||||
|
stream->io = avio_alloc_context(stream->buffer, sizeof(stream->buffer),
|
||||||
|
false, stream,
|
||||||
|
mpd_ffmpeg_stream_read, NULL,
|
||||||
|
input->seekable
|
||||||
|
? mpd_ffmpeg_stream_seek : NULL);
|
||||||
|
#else
|
||||||
stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
|
stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
|
||||||
false, stream,
|
false, stream,
|
||||||
mpd_ffmpeg_stream_read, NULL,
|
mpd_ffmpeg_stream_read, NULL,
|
||||||
input->seekable
|
input->seekable
|
||||||
? mpd_ffmpeg_stream_seek : NULL);
|
? mpd_ffmpeg_stream_seek : NULL);
|
||||||
|
#endif
|
||||||
if (stream->io == NULL) {
|
if (stream->io == NULL) {
|
||||||
g_free(stream);
|
g_free(stream);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -133,6 +152,33 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
|
|||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API compatibility wrapper for av_open_input_stream() and
|
||||||
|
* avformat_open_input().
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
mpd_ffmpeg_open_input(AVFormatContext **ic_ptr,
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
|
||||||
|
AVIOContext *pb,
|
||||||
|
#else
|
||||||
|
ByteIOContext *pb,
|
||||||
|
#endif
|
||||||
|
const char *filename,
|
||||||
|
AVInputFormat *fmt)
|
||||||
|
{
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,1,3)
|
||||||
|
AVFormatContext *context = avformat_alloc_context();
|
||||||
|
if (context == NULL)
|
||||||
|
return AVERROR(ENOMEM);
|
||||||
|
|
||||||
|
context->pb = pb;
|
||||||
|
*ic_ptr = context;
|
||||||
|
return avformat_open_input(ic_ptr, filename, fmt, NULL);
|
||||||
|
#else
|
||||||
|
return av_open_input_stream(ic_ptr, pb, filename, fmt, NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
|
mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
|
||||||
{
|
{
|
||||||
@@ -156,12 +202,17 @@ ffmpeg_find_audio_stream(const AVFormatContext *format_context)
|
|||||||
{
|
{
|
||||||
for (unsigned i = 0; i < format_context->nb_streams; ++i)
|
for (unsigned i = 0; i < format_context->nb_streams; ++i)
|
||||||
if (format_context->streams[i]->codec->codec_type ==
|
if (format_context->streams[i]->codec->codec_type ==
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0)
|
||||||
|
AVMEDIA_TYPE_AUDIO)
|
||||||
|
#else
|
||||||
CODEC_TYPE_AUDIO)
|
CODEC_TYPE_AUDIO)
|
||||||
|
#endif
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53,25,0)
|
||||||
/**
|
/**
|
||||||
* On some platforms, libavcodec wants the output buffer aligned to 16
|
* On some platforms, libavcodec wants the output buffer aligned to 16
|
||||||
* bytes (because it uses SSE/Altivec internally). This function
|
* bytes (because it uses SSE/Altivec internally). This function
|
||||||
@@ -176,6 +227,59 @@ align16(void *p, size_t *length_p)
|
|||||||
*length_p -= add;
|
*length_p -= add;
|
||||||
return (char *)p + add;
|
return (char *)p + add;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
G_GNUC_CONST
|
||||||
|
static double
|
||||||
|
time_from_ffmpeg(int64_t t, const AVRational time_base)
|
||||||
|
{
|
||||||
|
assert(t != (int64_t)AV_NOPTS_VALUE);
|
||||||
|
|
||||||
|
return (double)av_rescale_q(t, time_base, (AVRational){1, 1024})
|
||||||
|
/ (double)1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
G_GNUC_CONST
|
||||||
|
static int64_t
|
||||||
|
time_to_ffmpeg(double t, const AVRational time_base)
|
||||||
|
{
|
||||||
|
return av_rescale_q((int64_t)(t * 1024), (AVRational){1, 1024},
|
||||||
|
time_base);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
|
||||||
|
/**
|
||||||
|
* Copy PCM data from a AVFrame to an interleaved buffer.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
copy_interleave_frame(const AVCodecContext *codec_context,
|
||||||
|
const AVFrame *frame,
|
||||||
|
uint8_t *buffer, size_t buffer_size)
|
||||||
|
{
|
||||||
|
int plane_size;
|
||||||
|
const int data_size =
|
||||||
|
av_samples_get_buffer_size(&plane_size,
|
||||||
|
codec_context->channels,
|
||||||
|
frame->nb_samples,
|
||||||
|
codec_context->sample_fmt, 1);
|
||||||
|
if (buffer_size < (size_t)data_size)
|
||||||
|
/* buffer is too small - shouldn't happen */
|
||||||
|
return AVERROR(EINVAL);
|
||||||
|
|
||||||
|
if (av_sample_fmt_is_planar(codec_context->sample_fmt) &&
|
||||||
|
codec_context->channels > 1) {
|
||||||
|
for (int i = 0, channels = codec_context->channels;
|
||||||
|
i < channels; i++) {
|
||||||
|
memcpy(buffer, frame->extended_data[i], plane_size);
|
||||||
|
buffer += plane_size;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
memcpy(buffer, frame->extended_data[0], data_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data_size;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static enum decoder_command
|
static enum decoder_command
|
||||||
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
||||||
@@ -183,30 +287,60 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
|||||||
AVCodecContext *codec_context,
|
AVCodecContext *codec_context,
|
||||||
const AVRational *time_base)
|
const AVRational *time_base)
|
||||||
{
|
{
|
||||||
enum decoder_command cmd = DECODE_COMMAND_NONE;
|
|
||||||
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
|
|
||||||
int16_t *aligned_buffer;
|
|
||||||
size_t buffer_size;
|
|
||||||
int len, audio_size;
|
|
||||||
uint8_t *packet_data;
|
|
||||||
int packet_size;
|
|
||||||
|
|
||||||
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}));
|
|
||||||
|
|
||||||
packet_data = packet->data;
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
packet_size = packet->size;
|
AVPacket packet2 = *packet;
|
||||||
|
#else
|
||||||
|
const uint8_t *packet_data = packet->data;
|
||||||
|
int packet_size = packet->size;
|
||||||
|
#endif
|
||||||
|
|
||||||
buffer_size = sizeof(audio_buf);
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
|
||||||
aligned_buffer = align16(audio_buf, &buffer_size);
|
uint8_t aligned_buffer[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
|
||||||
|
const size_t buffer_size = sizeof(aligned_buffer);
|
||||||
|
#else
|
||||||
|
/* libavcodec < 0.8 needs an aligned buffer */
|
||||||
|
uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2 + 16];
|
||||||
|
size_t buffer_size = sizeof(audio_buf);
|
||||||
|
int16_t *aligned_buffer = align16(audio_buf, &buffer_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
while ((packet_size > 0) && (cmd == DECODE_COMMAND_NONE)) {
|
enum decoder_command cmd = DECODE_COMMAND_NONE;
|
||||||
audio_size = buffer_size;
|
while (
|
||||||
len = avcodec_decode_audio2(codec_context,
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
|
packet2.size > 0 &&
|
||||||
|
#else
|
||||||
|
packet_size > 0 &&
|
||||||
|
#endif
|
||||||
|
cmd == DECODE_COMMAND_NONE) {
|
||||||
|
int audio_size = buffer_size;
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,25,0)
|
||||||
|
AVFrame frame;
|
||||||
|
int got_frame = 0;
|
||||||
|
int len = avcodec_decode_audio4(codec_context,
|
||||||
|
&frame, &got_frame,
|
||||||
|
&packet2);
|
||||||
|
if (len >= 0 && got_frame) {
|
||||||
|
audio_size = copy_interleave_frame(codec_context,
|
||||||
|
&frame,
|
||||||
|
aligned_buffer,
|
||||||
|
buffer_size);
|
||||||
|
if (audio_size < 0)
|
||||||
|
len = audio_size;
|
||||||
|
} else if (len >= 0)
|
||||||
|
len = -1;
|
||||||
|
#elif LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
|
int len = avcodec_decode_audio3(codec_context,
|
||||||
|
aligned_buffer, &audio_size,
|
||||||
|
&packet2);
|
||||||
|
#else
|
||||||
|
int len = avcodec_decode_audio2(codec_context,
|
||||||
aligned_buffer, &audio_size,
|
aligned_buffer, &audio_size,
|
||||||
packet_data, packet_size);
|
packet_data, packet_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
/* if error, we skip the frame */
|
/* if error, we skip the frame */
|
||||||
@@ -214,8 +348,13 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52,25,0)
|
||||||
|
packet2.data += len;
|
||||||
|
packet2.size -= len;
|
||||||
|
#else
|
||||||
packet_data += len;
|
packet_data += len;
|
||||||
packet_size -= len;
|
packet_size -= len;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (audio_size <= 0)
|
if (audio_size <= 0)
|
||||||
continue;
|
continue;
|
||||||
@@ -230,12 +369,20 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
|
|||||||
static enum sample_format
|
static enum sample_format
|
||||||
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
|
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
|
||||||
{
|
{
|
||||||
#if LIBAVCODEC_VERSION_INT >= ((51<<16)+(41<<8)+0)
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(51, 41, 0)
|
||||||
switch (codec_context->sample_fmt) {
|
switch (codec_context->sample_fmt) {
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
|
||||||
|
case AV_SAMPLE_FMT_S16:
|
||||||
|
#else
|
||||||
case SAMPLE_FMT_S16:
|
case SAMPLE_FMT_S16:
|
||||||
|
#endif
|
||||||
return SAMPLE_FORMAT_S16;
|
return SAMPLE_FORMAT_S16;
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 94, 1)
|
||||||
|
case AV_SAMPLE_FMT_S32:
|
||||||
|
#else
|
||||||
case SAMPLE_FMT_S32:
|
case SAMPLE_FMT_S32:
|
||||||
|
#endif
|
||||||
return SAMPLE_FORMAT_S32;
|
return SAMPLE_FORMAT_S32;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -299,50 +446,59 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFormatContext *format_context;
|
|
||||||
AVCodecContext *codec_context;
|
|
||||||
AVCodec *codec;
|
|
||||||
int audio_stream;
|
|
||||||
|
|
||||||
//ffmpeg works with ours "fileops" helper
|
//ffmpeg works with ours "fileops" helper
|
||||||
if (av_open_input_stream(&format_context, stream->io, input->uri,
|
AVFormatContext *format_context = NULL;
|
||||||
input_format, NULL) != 0) {
|
if (mpd_ffmpeg_open_input(&format_context, stream->io, input->uri,
|
||||||
|
input_format) != 0) {
|
||||||
g_warning("Open failed\n");
|
g_warning("Open failed\n");
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (av_find_stream_info(format_context)<0) {
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
|
||||||
|
const int find_result =
|
||||||
|
avformat_find_stream_info(format_context, NULL);
|
||||||
|
#else
|
||||||
|
const int find_result = av_find_stream_info(format_context);
|
||||||
|
#endif
|
||||||
|
if (find_result < 0) {
|
||||||
g_warning("Couldn't find stream info\n");
|
g_warning("Couldn't find stream info\n");
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&format_context);
|
||||||
|
#else
|
||||||
av_close_input_stream(format_context);
|
av_close_input_stream(format_context);
|
||||||
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_stream = ffmpeg_find_audio_stream(format_context);
|
int audio_stream = ffmpeg_find_audio_stream(format_context);
|
||||||
if (audio_stream == -1) {
|
if (audio_stream == -1) {
|
||||||
g_warning("No audio stream inside\n");
|
g_warning("No audio stream inside\n");
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&format_context);
|
||||||
|
#else
|
||||||
av_close_input_stream(format_context);
|
av_close_input_stream(format_context);
|
||||||
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (codec_context->codec_name[0] != 0)
|
||||||
g_debug("codec '%s'", codec_context->codec_name);
|
g_debug("codec '%s'", codec_context->codec_name);
|
||||||
|
|
||||||
codec = avcodec_find_decoder(codec_context->codec_id);
|
AVCodec *codec = avcodec_find_decoder(codec_context->codec_id);
|
||||||
|
|
||||||
if (!codec) {
|
if (!codec) {
|
||||||
g_warning("Unsupported audio codec\n");
|
g_warning("Unsupported audio codec\n");
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&format_context);
|
||||||
|
#else
|
||||||
av_close_input_stream(format_context);
|
av_close_input_stream(format_context);
|
||||||
mpd_ffmpeg_stream_close(stream);
|
#endif
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avcodec_open(codec_context, codec)<0) {
|
|
||||||
g_warning("Could not open codec\n");
|
|
||||||
av_close_input_stream(format_context);
|
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -355,8 +511,32 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
|
|||||||
codec_context->channels, &error)) {
|
codec_context->channels, &error)) {
|
||||||
g_warning("%s", error->message);
|
g_warning("%s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
avcodec_close(codec_context);
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&format_context);
|
||||||
|
#else
|
||||||
av_close_input_stream(format_context);
|
av_close_input_stream(format_context);
|
||||||
|
#endif
|
||||||
|
mpd_ffmpeg_stream_close(stream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* the audio format must be read from AVCodecContext by now,
|
||||||
|
because avcodec_open() has been demonstrated to fill bogus
|
||||||
|
values into AVCodecContext.channels - a change that will be
|
||||||
|
reverted later by avcodec_decode_audio3() */
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53,6,0)
|
||||||
|
const int open_result = avcodec_open2(codec_context, codec, NULL);
|
||||||
|
#else
|
||||||
|
const int open_result = avcodec_open(codec_context, codec);
|
||||||
|
#endif
|
||||||
|
if (open_result < 0) {
|
||||||
|
g_warning("Could not open codec\n");
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&format_context);
|
||||||
|
#else
|
||||||
|
av_close_input_stream(format_context);
|
||||||
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -378,7 +558,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);
|
||||||
|
|
||||||
@@ -386,17 +566,25 @@ 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);
|
||||||
|
|
||||||
avcodec_close(codec_context);
|
avcodec_close(codec_context);
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&format_context);
|
||||||
|
#else
|
||||||
av_close_input_stream(format_context);
|
av_close_input_stream(format_context);
|
||||||
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,35 +595,44 @@ typedef struct ffmpeg_tag_map {
|
|||||||
} ffmpeg_tag_map;
|
} ffmpeg_tag_map;
|
||||||
|
|
||||||
static const ffmpeg_tag_map ffmpeg_tag_maps[] = {
|
static const ffmpeg_tag_map ffmpeg_tag_maps[] = {
|
||||||
{ TAG_TITLE, "title" },
|
#if LIBAVFORMAT_VERSION_INT < ((52<<16)+(50<<8))
|
||||||
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(50<<8))
|
|
||||||
{ TAG_ARTIST, "artist" },
|
|
||||||
{ TAG_DATE, "date" },
|
|
||||||
#else
|
|
||||||
{ TAG_ARTIST, "author" },
|
{ TAG_ARTIST, "author" },
|
||||||
{ TAG_DATE, "year" },
|
|
||||||
#endif
|
#endif
|
||||||
{ TAG_ALBUM, "album" },
|
{ TAG_DATE, "year" },
|
||||||
{ TAG_COMMENT, "comment" },
|
|
||||||
{ TAG_GENRE, "genre" },
|
|
||||||
{ TAG_TRACK, "track" },
|
|
||||||
{ TAG_ARTIST_SORT, "author-sort" },
|
{ TAG_ARTIST_SORT, "author-sort" },
|
||||||
{ TAG_ALBUM_ARTIST, "album_artist" },
|
{ TAG_ALBUM_ARTIST, "album_artist" },
|
||||||
{ TAG_ALBUM_ARTIST_SORT, "album_artist-sort" },
|
{ TAG_ALBUM_ARTIST_SORT, "album_artist-sort" },
|
||||||
{ TAG_COMPOSER, "composer" },
|
|
||||||
{ TAG_PERFORMER, "performer" },
|
/* sentinel */
|
||||||
{ TAG_DISC, "disc" },
|
{ TAG_NUM_OF_ITEM_TYPES, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53,1,0)
|
||||||
ffmpeg_copy_metadata(struct tag *tag, AVMetadata *m,
|
#define AVDictionary AVMetadata
|
||||||
const ffmpeg_tag_map tag_map)
|
#define AVDictionaryEntry AVMetadataTag
|
||||||
{
|
#define av_dict_get av_metadata_get
|
||||||
AVMetadataTag *mt = NULL;
|
#endif
|
||||||
|
|
||||||
while ((mt = av_metadata_get(m, tag_map.name, mt, 0)) != NULL)
|
static void
|
||||||
tag_add_item(tag, tag_map.type, mt->value);
|
ffmpeg_copy_metadata(struct tag *tag, enum tag_type type,
|
||||||
return mt != NULL;
|
AVDictionary *m, const char *name)
|
||||||
|
{
|
||||||
|
AVDictionaryEntry *mt = NULL;
|
||||||
|
|
||||||
|
while ((mt = av_dict_get(m, name, mt, 0)) != NULL)
|
||||||
|
tag_add_item(tag, type, mt->value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ffmpeg_copy_dictionary(struct tag *tag, AVDictionary *dict)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
|
||||||
|
ffmpeg_copy_metadata(tag, i,
|
||||||
|
dict, tag_item_names[i]);
|
||||||
|
|
||||||
|
for (const struct ffmpeg_tag_map *i = ffmpeg_tag_maps;
|
||||||
|
i->name != NULL; ++i)
|
||||||
|
ffmpeg_copy_metadata(tag, i->type, dict, i->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -452,15 +649,25 @@ ffmpeg_stream_tag(struct input_stream *is)
|
|||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
AVFormatContext *f;
|
AVFormatContext *f = NULL;
|
||||||
if (av_open_input_stream(&f, stream->io, is->uri,
|
if (mpd_ffmpeg_open_input(&f, stream->io, is->uri,
|
||||||
input_format, NULL) != 0) {
|
input_format) != 0) {
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (av_find_stream_info(f) < 0) {
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,6,0)
|
||||||
|
const int find_result =
|
||||||
|
avformat_find_stream_info(f, NULL);
|
||||||
|
#else
|
||||||
|
const int find_result = av_find_stream_info(f);
|
||||||
|
#endif
|
||||||
|
if (find_result < 0) {
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&f);
|
||||||
|
#else
|
||||||
av_close_input_stream(f);
|
av_close_input_stream(f);
|
||||||
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -472,14 +679,14 @@ ffmpeg_stream_tag(struct input_stream *is)
|
|||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
|
#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
|
||||||
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
|
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
|
||||||
|
#endif
|
||||||
|
|
||||||
for (unsigned i = 0; i < sizeof(ffmpeg_tag_maps)/sizeof(ffmpeg_tag_map); i++) {
|
ffmpeg_copy_dictionary(tag, f->metadata);
|
||||||
int idx = ffmpeg_find_audio_stream(f);
|
int idx = ffmpeg_find_audio_stream(f);
|
||||||
ffmpeg_copy_metadata(tag, f->metadata, ffmpeg_tag_maps[i]);
|
|
||||||
if (idx >= 0)
|
if (idx >= 0)
|
||||||
ffmpeg_copy_metadata(tag, f->streams[idx]->metadata, ffmpeg_tag_maps[i]);
|
ffmpeg_copy_dictionary(tag, f->streams[idx]->metadata);
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
if (f->author[0])
|
if (f->author[0])
|
||||||
tag_add_item(tag, TAG_ARTIST, f->author);
|
tag_add_item(tag, TAG_ARTIST, f->author);
|
||||||
@@ -506,7 +713,11 @@ ffmpeg_stream_tag(struct input_stream *is)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,17,0)
|
||||||
|
avformat_close_input(&f);
|
||||||
|
#else
|
||||||
av_close_input_stream(f);
|
av_close_input_stream(f);
|
||||||
|
#endif
|
||||||
mpd_ffmpeg_stream_close(stream);
|
mpd_ffmpeg_stream_close(stream);
|
||||||
|
|
||||||
return tag;
|
return tag;
|
||||||
|
@@ -81,7 +81,7 @@ flac_tell_cb(G_GNUC_UNUSED const FLAC__StreamDecoder *fd,
|
|||||||
struct flac_data *data = (struct flac_data *) fdata;
|
struct flac_data *data = (struct flac_data *) fdata;
|
||||||
|
|
||||||
if (!data->input_stream->seekable)
|
if (!data->input_stream->seekable)
|
||||||
return FLAC__STREAM_DECODER_SEEK_STATUS_UNSUPPORTED;
|
return FLAC__STREAM_DECODER_TELL_STATUS_UNSUPPORTED;
|
||||||
|
|
||||||
*offset = (long)(data->input_stream->offset);
|
*offset = (long)(data->input_stream->offset);
|
||||||
|
|
||||||
|
@@ -224,6 +224,7 @@ flac_tag_apply_metadata(struct tag *tag, const char *track,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case FLAC__METADATA_TYPE_STREAMINFO:
|
case FLAC__METADATA_TYPE_STREAMINFO:
|
||||||
|
if (block->data.stream_info.sample_rate > 0)
|
||||||
tag->time = flac_duration(&block->data.stream_info);
|
tag->time = flac_duration(&block->data.stream_info);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@
|
|||||||
#ifndef MPD_FLAC_METADATA_H
|
#ifndef MPD_FLAC_METADATA_H
|
||||||
#define MPD_FLAC_METADATA_H
|
#define MPD_FLAC_METADATA_H
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <FLAC/metadata.h>
|
#include <FLAC/metadata.h>
|
||||||
|
|
||||||
@@ -29,6 +30,8 @@ struct replay_gain_info;
|
|||||||
static inline unsigned
|
static inline unsigned
|
||||||
flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
|
flac_duration(const FLAC__StreamMetadata_StreamInfo *stream_info)
|
||||||
{
|
{
|
||||||
|
assert(stream_info->sample_rate > 0);
|
||||||
|
|
||||||
return (stream_info->total_samples + stream_info->sample_rate - 1) /
|
return (stream_info->total_samples + stream_info->sample_rate - 1) /
|
||||||
stream_info->sample_rate;
|
stream_info->sample_rate;
|
||||||
}
|
}
|
||||||
|
@@ -153,6 +153,9 @@ gme_file_decode(struct decoder *decoder, const char *path_fs)
|
|||||||
if((gme_err = gme_start_track(emu, song_num)) != NULL)
|
if((gme_err = gme_start_track(emu, song_num)) != NULL)
|
||||||
g_warning("%s", gme_err);
|
g_warning("%s", gme_err);
|
||||||
|
|
||||||
|
if(ti->length > 0)
|
||||||
|
gme_set_fade(emu, ti->length);
|
||||||
|
|
||||||
/* play */
|
/* play */
|
||||||
do {
|
do {
|
||||||
gme_err = gme_play(emu, GME_BUFFER_SAMPLES, buf);
|
gme_err = gme_play(emu, GME_BUFFER_SAMPLES, buf);
|
||||||
|
@@ -547,14 +547,14 @@ enum {
|
|||||||
XING_SCALE = 0x00000008L
|
XING_SCALE = 0x00000008L
|
||||||
};
|
};
|
||||||
|
|
||||||
struct version {
|
struct lame_version {
|
||||||
unsigned major;
|
unsigned major;
|
||||||
unsigned minor;
|
unsigned minor;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lame {
|
struct lame {
|
||||||
char encoder[10]; /* 9 byte encoder name/version ("LAME3.97b") */
|
char encoder[10]; /* 9 byte encoder name/version ("LAME3.97b") */
|
||||||
struct version version; /* struct containing just the version */
|
struct lame_version version; /* struct containing just the version */
|
||||||
float peak; /* replaygain peak */
|
float peak; /* replaygain peak */
|
||||||
float track_gain; /* replaygain track gain */
|
float track_gain; /* replaygain track gain */
|
||||||
float album_gain; /* replaygain album gain */
|
float album_gain; /* replaygain album gain */
|
||||||
|
@@ -94,6 +94,12 @@ mp4_read(void *user_data, void *buffer, uint32_t length)
|
|||||||
{
|
{
|
||||||
struct mp4ff_input_stream *mis = user_data;
|
struct mp4ff_input_stream *mis = user_data;
|
||||||
|
|
||||||
|
if (length == 0)
|
||||||
|
/* libmp4ff is known to attempt to read 0 bytes - make
|
||||||
|
this a special case, because the input_stream API
|
||||||
|
would not allow this */
|
||||||
|
return 0;
|
||||||
|
|
||||||
return decoder_read(mis->decoder, mis->input_stream, buffer, length);
|
return decoder_read(mis->decoder, mis->input_stream, buffer, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -153,7 +153,6 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
|
|||||||
mpc_uint32_t ret;
|
mpc_uint32_t ret;
|
||||||
int32_t chunk[G_N_ELEMENTS(sample_buffer)];
|
int32_t chunk[G_N_ELEMENTS(sample_buffer)];
|
||||||
long bit_rate = 0;
|
long bit_rate = 0;
|
||||||
mpc_uint32_t vbr_update_acc;
|
|
||||||
mpc_uint32_t vbr_update_bits;
|
mpc_uint32_t vbr_update_bits;
|
||||||
enum decoder_command cmd = DECODE_COMMAND_NONE;
|
enum decoder_command cmd = DECODE_COMMAND_NONE;
|
||||||
|
|
||||||
@@ -243,10 +242,11 @@ mpcdec_decode(struct decoder *mpd_decoder, struct input_stream *is)
|
|||||||
decoder_seek_error(mpd_decoder);
|
decoder_seek_error(mpd_decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
vbr_update_acc = 0;
|
|
||||||
vbr_update_bits = 0;
|
vbr_update_bits = 0;
|
||||||
|
|
||||||
#ifdef MPC_IS_OLD_API
|
#ifdef MPC_IS_OLD_API
|
||||||
|
mpc_uint32_t vbr_update_acc = 0;
|
||||||
|
|
||||||
ret = mpc_decoder_decode(&decoder, sample_buffer,
|
ret = mpc_decoder_decode(&decoder, sample_buffer,
|
||||||
&vbr_update_acc, &vbr_update_bits);
|
&vbr_update_acc, &vbr_update_bits);
|
||||||
if (ret == 0 || ret == (mpc_uint32_t)-1)
|
if (ret == 0 || ret == (mpc_uint32_t)-1)
|
||||||
|
@@ -34,9 +34,6 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "wavpack"
|
#define G_LOG_DOMAIN "wavpack"
|
||||||
|
|
||||||
/* pick 1020 since its devisible for 8,16,24, and 32-bit audio */
|
|
||||||
#define CHUNK_SIZE 1020
|
|
||||||
|
|
||||||
#define ERRORLEN 80
|
#define ERRORLEN 80
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
@@ -162,8 +159,6 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
|
|||||||
enum sample_format sample_format;
|
enum sample_format sample_format;
|
||||||
struct audio_format audio_format;
|
struct audio_format audio_format;
|
||||||
format_samples_t format_samples;
|
format_samples_t format_samples;
|
||||||
char chunk[CHUNK_SIZE];
|
|
||||||
int samples_requested, samples_got;
|
|
||||||
float total_time;
|
float total_time;
|
||||||
int bytes_per_sample, output_sample_size;
|
int bytes_per_sample, output_sample_size;
|
||||||
|
|
||||||
@@ -181,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;
|
||||||
@@ -193,12 +188,15 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
|
|||||||
output_sample_size = audio_format_frame_size(&audio_format);
|
output_sample_size = audio_format_frame_size(&audio_format);
|
||||||
|
|
||||||
/* wavpack gives us all kind of samples in a 32-bit space */
|
/* wavpack gives us all kind of samples in a 32-bit space */
|
||||||
samples_requested = sizeof(chunk) / (4 * audio_format.channels);
|
int32_t chunk[1024];
|
||||||
|
const uint32_t samples_requested = G_N_ELEMENTS(chunk) /
|
||||||
|
audio_format.channels;
|
||||||
|
|
||||||
decoder_initialized(decoder, &audio_format, can_seek, total_time);
|
decoder_initialized(decoder, &audio_format, can_seek, total_time);
|
||||||
|
|
||||||
do {
|
enum decoder_command cmd = decoder_get_command(decoder);
|
||||||
if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) {
|
while (cmd != DECODE_COMMAND_STOP) {
|
||||||
|
if (cmd == DECODE_COMMAND_SEEK) {
|
||||||
if (can_seek) {
|
if (can_seek) {
|
||||||
unsigned where = decoder_seek_where(decoder) *
|
unsigned where = decoder_seek_where(decoder) *
|
||||||
audio_format.sample_rate;
|
audio_format.sample_rate;
|
||||||
@@ -213,29 +211,20 @@ wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decoder_get_command(decoder) == DECODE_COMMAND_STOP) {
|
uint32_t samples_got = WavpackUnpackSamples(wpc, chunk,
|
||||||
|
samples_requested);
|
||||||
|
if (samples_got == 0)
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
samples_got = WavpackUnpackSamples(
|
int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 +
|
||||||
wpc, (int32_t *)chunk, samples_requested
|
0.5);
|
||||||
);
|
format_samples(bytes_per_sample, chunk,
|
||||||
if (samples_got > 0) {
|
samples_got * audio_format.channels);
|
||||||
int bitrate = (int)(WavpackGetInstantBitrate(wpc) /
|
|
||||||
1000 + 0.5);
|
|
||||||
|
|
||||||
format_samples(
|
cmd = decoder_data(decoder, NULL, chunk,
|
||||||
bytes_per_sample, chunk,
|
|
||||||
samples_got * audio_format.channels
|
|
||||||
);
|
|
||||||
|
|
||||||
decoder_data(
|
|
||||||
decoder, NULL, chunk,
|
|
||||||
samples_got * output_sample_size,
|
samples_got * output_sample_size,
|
||||||
bitrate
|
bitrate);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} while (samples_got > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -78,15 +78,76 @@ 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 (dc->state != DECODE_STATE_DECODE)
|
||||||
|
/* wait until the decoder has finished initialisation
|
||||||
|
(reading file headers etc.) before emitting the
|
||||||
|
virtual "SEEK" command */
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (decoder->initial_seek_running)
|
||||||
|
/* initial seek has already begun - override any other
|
||||||
|
command */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (decoder->initial_seek_pending) {
|
||||||
|
if (!dc->seekable) {
|
||||||
|
/* seeking is not possible */
|
||||||
|
decoder->initial_seek_pending = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dc->command == DECODE_COMMAND_NONE) {
|
||||||
|
/* begin initial seek */
|
||||||
|
|
||||||
|
decoder->initial_seek_pending = false;
|
||||||
|
decoder->initial_seek_running = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* skip initial seek when there's another command
|
||||||
|
(e.g. STOP) */
|
||||||
|
|
||||||
|
decoder->initial_seek_pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current decoder command. May return a "virtual"
|
||||||
|
* synthesized command, e.g. to seek to the beginning of the CUE
|
||||||
|
* track.
|
||||||
|
*/
|
||||||
|
G_GNUC_PURE
|
||||||
|
static enum decoder_command
|
||||||
|
decoder_get_virtual_command(struct decoder *decoder)
|
||||||
|
{
|
||||||
|
const struct decoder_control *dc = decoder->dc;
|
||||||
|
assert(dc->pipe != NULL);
|
||||||
|
|
||||||
|
if (decoder_prepare_initial_seek(decoder))
|
||||||
|
return DECODE_COMMAND_SEEK;
|
||||||
|
|
||||||
return dc->command;
|
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 +155,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 +198,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 +214,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 +356,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 +443,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 +474,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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
#include "decoder_list.h"
|
#include "decoder_list.h"
|
||||||
#include "decoder_plugin.h"
|
#include "decoder_plugin.h"
|
||||||
#include "decoder_api.h"
|
#include "decoder_api.h"
|
||||||
|
#include "replay_gain_ape.h"
|
||||||
#include "input_stream.h"
|
#include "input_stream.h"
|
||||||
#include "player_control.h"
|
#include "player_control.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdio.h> /* for SEEK_SET */
|
||||||
|
|
||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "decoder_thread"
|
#define G_LOG_DOMAIN "decoder_thread"
|
||||||
@@ -297,6 +299,18 @@ decoder_run_stream(struct decoder *decoder, const char *uri)
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to load replay gain data, and pass it to
|
||||||
|
* decoder_replay_gain().
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
decoder_load_replay_gain(struct decoder *decoder, const char *path_fs)
|
||||||
|
{
|
||||||
|
struct replay_gain_info info;
|
||||||
|
if (replay_gain_ape_read(path_fs, &info))
|
||||||
|
decoder_replay_gain(decoder, &info);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try decoding a file.
|
* Try decoding a file.
|
||||||
*/
|
*/
|
||||||
@@ -312,6 +326,8 @@ decoder_run_file(struct decoder *decoder, const char *path_fs)
|
|||||||
|
|
||||||
decoder_unlock(dc);
|
decoder_unlock(dc);
|
||||||
|
|
||||||
|
decoder_load_replay_gain(decoder, path_fs);
|
||||||
|
|
||||||
while ((plugin = decoder_plugin_from_suffix(suffix, plugin)) != NULL) {
|
while ((plugin = decoder_plugin_from_suffix(suffix, plugin)) != NULL) {
|
||||||
if (plugin->file_decode != NULL) {
|
if (plugin->file_decode != NULL) {
|
||||||
decoder_lock(dc);
|
decoder_lock(dc);
|
||||||
@@ -354,6 +370,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;
|
||||||
|
|
||||||
|
@@ -68,7 +68,15 @@ directory_free(struct directory *directory)
|
|||||||
const char *
|
const char *
|
||||||
directory_get_name(const struct directory *directory)
|
directory_get_name(const struct directory *directory)
|
||||||
{
|
{
|
||||||
return g_basename(directory->path);
|
assert(!directory_is_root(directory));
|
||||||
|
assert(directory->path != NULL);
|
||||||
|
|
||||||
|
const char *slash = strrchr(directory->path, '/');
|
||||||
|
assert((slash == NULL) == directory_is_root(directory->parent));
|
||||||
|
|
||||||
|
return slash != NULL
|
||||||
|
? slash + 1
|
||||||
|
: directory->path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@@ -43,7 +43,7 @@ struct directory {
|
|||||||
time_t mtime;
|
time_t mtime;
|
||||||
ino_t inode;
|
ino_t inode;
|
||||||
dev_t device;
|
dev_t device;
|
||||||
unsigned stat; /* not needed if ino_t == dev_t == 0 is impossible */
|
bool have_stat; /* not needed if ino_t == dev_t == 0 is impossible */
|
||||||
char path[sizeof(long)];
|
char path[sizeof(long)];
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -62,7 +62,8 @@ directory_free(struct directory *directory);
|
|||||||
static inline bool
|
static inline bool
|
||||||
directory_is_empty(const struct directory *directory)
|
directory_is_empty(const struct directory *directory)
|
||||||
{
|
{
|
||||||
return directory->children.nr == 0 && directory->songs.nr == 0;
|
return directory->children.nr == 0 && directory->songs.nr == 0 &&
|
||||||
|
playlist_vector_is_empty(&directory->playlists);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const char *
|
static inline const char *
|
||||||
|
@@ -22,6 +22,8 @@
|
|||||||
#include "encoder_plugin.h"
|
#include "encoder_plugin.h"
|
||||||
#include "audio_format.h"
|
#include "audio_format.h"
|
||||||
#include "pcm_buffer.h"
|
#include "pcm_buffer.h"
|
||||||
|
#include "fifo_buffer.h"
|
||||||
|
#include "growing_fifo.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -38,8 +40,11 @@ struct flac_encoder {
|
|||||||
|
|
||||||
struct pcm_buffer expand_buffer;
|
struct pcm_buffer expand_buffer;
|
||||||
|
|
||||||
struct pcm_buffer buffer;
|
/**
|
||||||
size_t buffer_length;
|
* This buffer will hold encoded data from libFLAC until it is
|
||||||
|
* picked up with flac_encoder_read().
|
||||||
|
*/
|
||||||
|
struct fifo_buffer *output_buffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct encoder_plugin flac_encoder_plugin;
|
extern const struct encoder_plugin flac_encoder_plugin;
|
||||||
@@ -140,11 +145,8 @@ flac_write_callback(G_GNUC_UNUSED const FLAC__StreamEncoder *fse,
|
|||||||
{
|
{
|
||||||
struct flac_encoder *encoder = (struct flac_encoder *) client_data;
|
struct flac_encoder *encoder = (struct flac_encoder *) client_data;
|
||||||
|
|
||||||
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + bytes);
|
|
||||||
|
|
||||||
//transfer data to buffer
|
//transfer data to buffer
|
||||||
memcpy( buffer + encoder->buffer_length, data, bytes);
|
growing_fifo_append(&encoder->output_buffer, data, bytes);
|
||||||
encoder->buffer_length += bytes;
|
|
||||||
|
|
||||||
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
|
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
|
||||||
}
|
}
|
||||||
@@ -156,8 +158,8 @@ flac_encoder_close(struct encoder *_encoder)
|
|||||||
|
|
||||||
FLAC__stream_encoder_delete(encoder->fse);
|
FLAC__stream_encoder_delete(encoder->fse);
|
||||||
|
|
||||||
pcm_buffer_deinit(&encoder->buffer);
|
|
||||||
pcm_buffer_deinit(&encoder->expand_buffer);
|
pcm_buffer_deinit(&encoder->expand_buffer);
|
||||||
|
fifo_buffer_free(encoder->output_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -201,10 +203,10 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
encoder->buffer_length = 0;
|
|
||||||
pcm_buffer_init(&encoder->buffer);
|
|
||||||
pcm_buffer_init(&encoder->expand_buffer);
|
pcm_buffer_init(&encoder->expand_buffer);
|
||||||
|
|
||||||
|
encoder->output_buffer = growing_fifo_new();
|
||||||
|
|
||||||
/* this immediatelly outputs data throught callback */
|
/* this immediatelly outputs data throught callback */
|
||||||
|
|
||||||
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
|
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
|
||||||
@@ -325,16 +327,18 @@ static size_t
|
|||||||
flac_encoder_read(struct encoder *_encoder, void *dest, size_t length)
|
flac_encoder_read(struct encoder *_encoder, void *dest, size_t length)
|
||||||
{
|
{
|
||||||
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
|
struct flac_encoder *encoder = (struct flac_encoder *)_encoder;
|
||||||
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length);
|
|
||||||
|
|
||||||
if (length > encoder->buffer_length)
|
size_t max_length;
|
||||||
length = encoder->buffer_length;
|
const char *src = fifo_buffer_read(encoder->output_buffer,
|
||||||
|
&max_length);
|
||||||
|
if (src == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
memcpy(dest, buffer, length);
|
if (length > max_length)
|
||||||
|
length = max_length;
|
||||||
encoder->buffer_length -= length;
|
|
||||||
memmove(buffer, buffer + length, encoder->buffer_length);
|
|
||||||
|
|
||||||
|
memcpy(dest, src, length);
|
||||||
|
fifo_buffer_consume(encoder->output_buffer, length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,6 +354,7 @@ const struct encoder_plugin flac_encoder_plugin = {
|
|||||||
.finish = flac_encoder_finish,
|
.finish = flac_encoder_finish,
|
||||||
.open = flac_encoder_open,
|
.open = flac_encoder_open,
|
||||||
.close = flac_encoder_close,
|
.close = flac_encoder_close,
|
||||||
|
.end = flac_encoder_flush,
|
||||||
.flush = flac_encoder_flush,
|
.flush = flac_encoder_flush,
|
||||||
.write = flac_encoder_write,
|
.write = flac_encoder_write,
|
||||||
.read = flac_encoder_read,
|
.read = flac_encoder_read,
|
||||||
|
@@ -170,6 +170,13 @@ lame_encoder_setup(struct lame_encoder *encoder, GError **error)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (0 != lame_set_out_samplerate(encoder->gfp,
|
||||||
|
encoder->audio_format.sample_rate)) {
|
||||||
|
g_set_error(error, lame_encoder_quark(), 0,
|
||||||
|
"error setting lame out sample rate");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (0 > lame_init_params(encoder->gfp)) {
|
if (0 > lame_init_params(encoder->gfp)) {
|
||||||
g_set_error(error, lame_encoder_quark(), 0,
|
g_set_error(error, lame_encoder_quark(), 0,
|
||||||
"error initializing lame params");
|
"error initializing lame params");
|
||||||
|
@@ -20,7 +20,8 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "encoder_api.h"
|
#include "encoder_api.h"
|
||||||
#include "encoder_plugin.h"
|
#include "encoder_plugin.h"
|
||||||
#include "pcm_buffer.h"
|
#include "fifo_buffer.h"
|
||||||
|
#include "growing_fifo.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -28,8 +29,7 @@
|
|||||||
struct null_encoder {
|
struct null_encoder {
|
||||||
struct encoder encoder;
|
struct encoder encoder;
|
||||||
|
|
||||||
struct pcm_buffer buffer;
|
struct fifo_buffer *buffer;
|
||||||
size_t buffer_length;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct encoder_plugin null_encoder_plugin;
|
extern const struct encoder_plugin null_encoder_plugin;
|
||||||
@@ -65,7 +65,7 @@ null_encoder_close(struct encoder *_encoder)
|
|||||||
{
|
{
|
||||||
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
||||||
|
|
||||||
pcm_buffer_deinit(&encoder->buffer);
|
fifo_buffer_free(encoder->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -76,9 +76,7 @@ null_encoder_open(struct encoder *_encoder,
|
|||||||
{
|
{
|
||||||
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
||||||
|
|
||||||
encoder->buffer_length = 0;
|
encoder->buffer = growing_fifo_new();
|
||||||
pcm_buffer_init(&encoder->buffer);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,28 +86,26 @@ null_encoder_write(struct encoder *_encoder,
|
|||||||
G_GNUC_UNUSED GError **error)
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
||||||
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + length);
|
|
||||||
|
|
||||||
memcpy(buffer+encoder->buffer_length, data, length);
|
growing_fifo_append(&encoder->buffer, data, length);
|
||||||
|
return length;
|
||||||
encoder->buffer_length += length;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
null_encoder_read(struct encoder *_encoder, void *dest, size_t length)
|
null_encoder_read(struct encoder *_encoder, void *dest, size_t length)
|
||||||
{
|
{
|
||||||
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
struct null_encoder *encoder = (struct null_encoder *)_encoder;
|
||||||
char *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length);
|
|
||||||
|
|
||||||
if (length > encoder->buffer_length)
|
size_t max_length;
|
||||||
length = encoder->buffer_length;
|
const void *src = fifo_buffer_read(encoder->buffer, &max_length);
|
||||||
|
if (src == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
memcpy(dest, buffer, length);
|
if (length > max_length)
|
||||||
|
length = max_length;
|
||||||
encoder->buffer_length -= length;
|
|
||||||
memmove(buffer, buffer + length, encoder->buffer_length);
|
|
||||||
|
|
||||||
|
memcpy(dest, src, length);
|
||||||
|
fifo_buffer_consume(encoder->buffer, length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -300,6 +300,7 @@ const struct encoder_plugin twolame_encoder_plugin = {
|
|||||||
.finish = twolame_encoder_finish,
|
.finish = twolame_encoder_finish,
|
||||||
.open = twolame_encoder_open,
|
.open = twolame_encoder_open,
|
||||||
.close = twolame_encoder_close,
|
.close = twolame_encoder_close,
|
||||||
|
.end = twolame_encoder_flush,
|
||||||
.flush = twolame_encoder_flush,
|
.flush = twolame_encoder_flush,
|
||||||
.write = twolame_encoder_write,
|
.write = twolame_encoder_write,
|
||||||
.read = twolame_encoder_read,
|
.read = twolame_encoder_read,
|
||||||
|
@@ -266,6 +266,15 @@ vorbis_encoder_flush(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
|
|||||||
{
|
{
|
||||||
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||||
|
|
||||||
|
encoder->flush = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vorbis_encoder_pre_tag(struct encoder *_encoder, G_GNUC_UNUSED GError **error)
|
||||||
|
{
|
||||||
|
struct vorbis_encoder *encoder = (struct vorbis_encoder *)_encoder;
|
||||||
|
|
||||||
vorbis_analysis_wrote(&encoder->vd, 0);
|
vorbis_analysis_wrote(&encoder->vd, 0);
|
||||||
vorbis_encoder_blockout(encoder);
|
vorbis_encoder_blockout(encoder);
|
||||||
|
|
||||||
@@ -364,6 +373,7 @@ vorbis_encoder_read(struct encoder *_encoder, void *_dest, size_t length)
|
|||||||
if (ret == 0 && encoder->flush) {
|
if (ret == 0 && encoder->flush) {
|
||||||
encoder->flush = false;
|
encoder->flush = false;
|
||||||
ret = ogg_stream_flush(&encoder->os, &page);
|
ret = ogg_stream_flush(&encoder->os, &page);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
@@ -395,7 +405,9 @@ const struct encoder_plugin vorbis_encoder_plugin = {
|
|||||||
.finish = vorbis_encoder_finish,
|
.finish = vorbis_encoder_finish,
|
||||||
.open = vorbis_encoder_open,
|
.open = vorbis_encoder_open,
|
||||||
.close = vorbis_encoder_close,
|
.close = vorbis_encoder_close,
|
||||||
|
.end = vorbis_encoder_pre_tag,
|
||||||
.flush = vorbis_encoder_flush,
|
.flush = vorbis_encoder_flush,
|
||||||
|
.pre_tag = vorbis_encoder_pre_tag,
|
||||||
.tag = vorbis_encoder_tag,
|
.tag = vorbis_encoder_tag,
|
||||||
.write = vorbis_encoder_write,
|
.write = vorbis_encoder_write,
|
||||||
.read = vorbis_encoder_read,
|
.read = vorbis_encoder_read,
|
||||||
|
@@ -20,7 +20,8 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "encoder_api.h"
|
#include "encoder_api.h"
|
||||||
#include "encoder_plugin.h"
|
#include "encoder_plugin.h"
|
||||||
#include "pcm_buffer.h"
|
#include "fifo_buffer.h"
|
||||||
|
#include "growing_fifo.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -29,8 +30,7 @@ struct wave_encoder {
|
|||||||
struct encoder encoder;
|
struct encoder encoder;
|
||||||
unsigned bits;
|
unsigned bits;
|
||||||
|
|
||||||
struct pcm_buffer buffer;
|
struct fifo_buffer *buffer;
|
||||||
size_t buffer_length;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wave_header {
|
struct wave_header {
|
||||||
@@ -92,7 +92,6 @@ wave_encoder_init(G_GNUC_UNUSED const struct config_param *param,
|
|||||||
|
|
||||||
encoder = g_new(struct wave_encoder, 1);
|
encoder = g_new(struct wave_encoder, 1);
|
||||||
encoder_struct_init(&encoder->encoder, &wave_encoder_plugin);
|
encoder_struct_init(&encoder->encoder, &wave_encoder_plugin);
|
||||||
pcm_buffer_init(&encoder->buffer);
|
|
||||||
|
|
||||||
return &encoder->encoder;
|
return &encoder->encoder;
|
||||||
}
|
}
|
||||||
@@ -102,7 +101,6 @@ wave_encoder_finish(struct encoder *_encoder)
|
|||||||
{
|
{
|
||||||
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
||||||
|
|
||||||
pcm_buffer_deinit(&encoder->buffer);
|
|
||||||
g_free(encoder);
|
g_free(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,7 +110,6 @@ wave_encoder_open(struct encoder *_encoder,
|
|||||||
G_GNUC_UNUSED GError **error)
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
assert(audio_format_valid(audio_format));
|
assert(audio_format_valid(audio_format));
|
||||||
|
|
||||||
@@ -125,6 +122,11 @@ wave_encoder_open(struct encoder *_encoder,
|
|||||||
encoder->bits = 16;
|
encoder->bits = 16;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SAMPLE_FORMAT_S24:
|
||||||
|
audio_format->format = SAMPLE_FORMAT_S24_P32;
|
||||||
|
encoder->bits = 24;
|
||||||
|
break;
|
||||||
|
|
||||||
case SAMPLE_FORMAT_S24_P32:
|
case SAMPLE_FORMAT_S24_P32:
|
||||||
encoder->bits = 24;
|
encoder->bits = 24;
|
||||||
break;
|
break;
|
||||||
@@ -139,19 +141,29 @@ wave_encoder_open(struct encoder *_encoder,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer = pcm_buffer_get(&encoder->buffer, sizeof(struct wave_header) );
|
encoder->buffer = growing_fifo_new();
|
||||||
|
struct wave_header *header =
|
||||||
|
growing_fifo_write(&encoder->buffer, sizeof(*header));
|
||||||
|
|
||||||
/* create PCM wave header in initial buffer */
|
/* create PCM wave header in initial buffer */
|
||||||
fill_wave_header((struct wave_header *) buffer,
|
fill_wave_header(header,
|
||||||
audio_format->channels,
|
audio_format->channels,
|
||||||
encoder->bits,
|
encoder->bits,
|
||||||
audio_format->sample_rate,
|
audio_format->sample_rate,
|
||||||
(encoder->bits / 8) * audio_format->channels );
|
(encoder->bits / 8) * audio_format->channels );
|
||||||
|
fifo_buffer_append(encoder->buffer, sizeof(*header));
|
||||||
|
|
||||||
encoder->buffer_length = sizeof(struct wave_header);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wave_encoder_close(struct encoder *_encoder)
|
||||||
|
{
|
||||||
|
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
||||||
|
|
||||||
|
fifo_buffer_free(encoder->buffer);
|
||||||
|
}
|
||||||
|
|
||||||
static inline size_t
|
static inline size_t
|
||||||
pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
|
pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
|
||||||
{
|
{
|
||||||
@@ -198,9 +210,8 @@ wave_encoder_write(struct encoder *_encoder,
|
|||||||
G_GNUC_UNUSED GError **error)
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
||||||
void *dst;
|
|
||||||
|
|
||||||
dst = pcm_buffer_get(&encoder->buffer, encoder->buffer_length + length);
|
void *dst = growing_fifo_write(&encoder->buffer, length);
|
||||||
|
|
||||||
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
#if (G_BYTE_ORDER == G_LITTLE_ENDIAN)
|
||||||
switch (encoder->bits) {
|
switch (encoder->bits) {
|
||||||
@@ -232,7 +243,7 @@ wave_encoder_write(struct encoder *_encoder,
|
|||||||
#error G_BYTE_ORDER set to G_PDP_ENDIAN is not supported by wave_encoder
|
#error G_BYTE_ORDER set to G_PDP_ENDIAN is not supported by wave_encoder
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
encoder->buffer_length += length;
|
fifo_buffer_append(encoder->buffer, length);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,16 +251,17 @@ static size_t
|
|||||||
wave_encoder_read(struct encoder *_encoder, void *dest, size_t length)
|
wave_encoder_read(struct encoder *_encoder, void *dest, size_t length)
|
||||||
{
|
{
|
||||||
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
struct wave_encoder *encoder = (struct wave_encoder *)_encoder;
|
||||||
uint8_t *buffer = pcm_buffer_get(&encoder->buffer, encoder->buffer_length );
|
|
||||||
|
|
||||||
if (length > encoder->buffer_length)
|
size_t max_length;
|
||||||
length = encoder->buffer_length;
|
const void *src = fifo_buffer_read(encoder->buffer, &max_length);
|
||||||
|
if (src == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
memcpy(dest, buffer, length);
|
if (length > max_length)
|
||||||
|
length = max_length;
|
||||||
encoder->buffer_length -= length;
|
|
||||||
memmove(buffer, buffer + length, encoder->buffer_length);
|
|
||||||
|
|
||||||
|
memcpy(dest, src, length);
|
||||||
|
fifo_buffer_consume(encoder->buffer, length);
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,6 +276,7 @@ const struct encoder_plugin wave_encoder_plugin = {
|
|||||||
.init = wave_encoder_init,
|
.init = wave_encoder_init,
|
||||||
.finish = wave_encoder_finish,
|
.finish = wave_encoder_finish,
|
||||||
.open = wave_encoder_open,
|
.open = wave_encoder_open,
|
||||||
|
.close = wave_encoder_close,
|
||||||
.write = wave_encoder_write,
|
.write = wave_encoder_write,
|
||||||
.read = wave_encoder_read,
|
.read = wave_encoder_read,
|
||||||
.get_mime_type = wave_encoder_get_mime_type,
|
.get_mime_type = wave_encoder_get_mime_type,
|
||||||
|
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
@@ -32,6 +33,10 @@ struct tag;
|
|||||||
|
|
||||||
struct encoder {
|
struct encoder {
|
||||||
const struct encoder_plugin *plugin;
|
const struct encoder_plugin *plugin;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
bool open, pre_tag, tag, end;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
struct encoder_plugin {
|
struct encoder_plugin {
|
||||||
@@ -48,8 +53,12 @@ struct encoder_plugin {
|
|||||||
|
|
||||||
void (*close)(struct encoder *encoder);
|
void (*close)(struct encoder *encoder);
|
||||||
|
|
||||||
|
bool (*end)(struct encoder *encoder, GError **error);
|
||||||
|
|
||||||
bool (*flush)(struct encoder *encoder, GError **error);
|
bool (*flush)(struct encoder *encoder, GError **error);
|
||||||
|
|
||||||
|
bool (*pre_tag)(struct encoder *encoder, GError **error);
|
||||||
|
|
||||||
bool (*tag)(struct encoder *encoder, const struct tag *tag,
|
bool (*tag)(struct encoder *encoder, const struct tag *tag,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
@@ -71,6 +80,10 @@ encoder_struct_init(struct encoder *encoder,
|
|||||||
const struct encoder_plugin *plugin)
|
const struct encoder_plugin *plugin)
|
||||||
{
|
{
|
||||||
encoder->plugin = plugin;
|
encoder->plugin = plugin;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
encoder->open = false;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,6 +109,8 @@ encoder_init(const struct encoder_plugin *plugin,
|
|||||||
static inline void
|
static inline void
|
||||||
encoder_finish(struct encoder *encoder)
|
encoder_finish(struct encoder *encoder)
|
||||||
{
|
{
|
||||||
|
assert(!encoder->open);
|
||||||
|
|
||||||
encoder->plugin->finish(encoder);
|
encoder->plugin->finish(encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +129,14 @@ static inline bool
|
|||||||
encoder_open(struct encoder *encoder, struct audio_format *audio_format,
|
encoder_open(struct encoder *encoder, struct audio_format *audio_format,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
return encoder->plugin->open(encoder, audio_format, error);
|
assert(!encoder->open);
|
||||||
|
|
||||||
|
bool success = encoder->plugin->open(encoder, audio_format, error);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
encoder->open = success;
|
||||||
|
encoder->pre_tag = encoder->tag = encoder->end = false;
|
||||||
|
#endif
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,8 +148,43 @@ encoder_open(struct encoder *encoder, struct audio_format *audio_format,
|
|||||||
static inline void
|
static inline void
|
||||||
encoder_close(struct encoder *encoder)
|
encoder_close(struct encoder *encoder)
|
||||||
{
|
{
|
||||||
|
assert(encoder->open);
|
||||||
|
|
||||||
if (encoder->plugin->close != NULL)
|
if (encoder->plugin->close != NULL)
|
||||||
encoder->plugin->close(encoder);
|
encoder->plugin->close(encoder);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
encoder->open = false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the stream: flushes the encoder object, generate an
|
||||||
|
* end-of-stream marker (if applicable), make everything which might
|
||||||
|
* currently be buffered available by encoder_read().
|
||||||
|
*
|
||||||
|
* After this function has been called, the encoder may not be usable
|
||||||
|
* for more data, and only encoder_read() and encoder_close() can be
|
||||||
|
* called.
|
||||||
|
*
|
||||||
|
* @param encoder the encoder
|
||||||
|
* @param error location to store the error occuring, or NULL to ignore errors.
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
encoder_end(struct encoder *encoder, GError **error)
|
||||||
|
{
|
||||||
|
assert(encoder->open);
|
||||||
|
assert(!encoder->end);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
encoder->end = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* this method is optional */
|
||||||
|
return encoder->plugin->end != NULL
|
||||||
|
? encoder->plugin->end(encoder, error)
|
||||||
|
: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -141,15 +198,52 @@ encoder_close(struct encoder *encoder)
|
|||||||
static inline bool
|
static inline bool
|
||||||
encoder_flush(struct encoder *encoder, GError **error)
|
encoder_flush(struct encoder *encoder, GError **error)
|
||||||
{
|
{
|
||||||
|
assert(encoder->open);
|
||||||
|
assert(!encoder->pre_tag);
|
||||||
|
assert(!encoder->tag);
|
||||||
|
assert(!encoder->end);
|
||||||
|
|
||||||
/* this method is optional */
|
/* this method is optional */
|
||||||
return encoder->plugin->flush != NULL
|
return encoder->plugin->flush != NULL
|
||||||
? encoder->plugin->flush(encoder, error)
|
? encoder->plugin->flush(encoder, error)
|
||||||
: true;
|
: true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare for sending a tag to the encoder. This is used by some
|
||||||
|
* encoders to flush the previous sub-stream, in preparation to begin
|
||||||
|
* a new one.
|
||||||
|
*
|
||||||
|
* @param encoder the encoder
|
||||||
|
* @param tag the tag object
|
||||||
|
* @param error location to store the error occuring, or NULL to ignore errors.
|
||||||
|
* @return true on success
|
||||||
|
*/
|
||||||
|
static inline bool
|
||||||
|
encoder_pre_tag(struct encoder *encoder, GError **error)
|
||||||
|
{
|
||||||
|
assert(encoder->open);
|
||||||
|
assert(!encoder->pre_tag);
|
||||||
|
assert(!encoder->tag);
|
||||||
|
assert(!encoder->end);
|
||||||
|
|
||||||
|
/* this method is optional */
|
||||||
|
bool success = encoder->plugin->pre_tag != NULL
|
||||||
|
? encoder->plugin->pre_tag(encoder, error)
|
||||||
|
: true;
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
encoder->pre_tag = success;
|
||||||
|
#endif
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a tag to the encoder.
|
* Sends a tag to the encoder.
|
||||||
*
|
*
|
||||||
|
* Instructions: call encoder_pre_tag(); then obtain flushed data with
|
||||||
|
* encoder_read(); finally call encoder_tag().
|
||||||
|
*
|
||||||
* @param encoder the encoder
|
* @param encoder the encoder
|
||||||
* @param tag the tag object
|
* @param tag the tag object
|
||||||
* @param error location to store the error occuring, or NULL to ignore errors.
|
* @param error location to store the error occuring, or NULL to ignore errors.
|
||||||
@@ -158,6 +252,15 @@ encoder_flush(struct encoder *encoder, GError **error)
|
|||||||
static inline bool
|
static inline bool
|
||||||
encoder_tag(struct encoder *encoder, const struct tag *tag, GError **error)
|
encoder_tag(struct encoder *encoder, const struct tag *tag, GError **error)
|
||||||
{
|
{
|
||||||
|
assert(encoder->open);
|
||||||
|
assert(!encoder->pre_tag);
|
||||||
|
assert(encoder->tag);
|
||||||
|
assert(!encoder->end);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
encoder->tag = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* this method is optional */
|
/* this method is optional */
|
||||||
return encoder->plugin->tag != NULL
|
return encoder->plugin->tag != NULL
|
||||||
? encoder->plugin->tag(encoder, tag, error)
|
? encoder->plugin->tag(encoder, tag, error)
|
||||||
@@ -177,6 +280,11 @@ static inline bool
|
|||||||
encoder_write(struct encoder *encoder, const void *data, size_t length,
|
encoder_write(struct encoder *encoder, const void *data, size_t length,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
|
assert(encoder->open);
|
||||||
|
assert(!encoder->pre_tag);
|
||||||
|
assert(!encoder->tag);
|
||||||
|
assert(!encoder->end);
|
||||||
|
|
||||||
return encoder->plugin->write(encoder, data, length, error);
|
return encoder->plugin->write(encoder, data, length, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,6 +299,16 @@ encoder_write(struct encoder *encoder, const void *data, size_t length,
|
|||||||
static inline size_t
|
static inline size_t
|
||||||
encoder_read(struct encoder *encoder, void *dest, size_t length)
|
encoder_read(struct encoder *encoder, void *dest, size_t length)
|
||||||
{
|
{
|
||||||
|
assert(encoder->open);
|
||||||
|
assert(!encoder->pre_tag || !encoder->tag);
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
if (encoder->pre_tag) {
|
||||||
|
encoder->pre_tag = false;
|
||||||
|
encoder->tag = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return encoder->plugin->read(encoder, dest, length);
|
return encoder->plugin->read(encoder, dest, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -159,5 +159,6 @@ void event_pipe_emit_fast(enum pipe_event event)
|
|||||||
assert((unsigned)event < PIPE_EVENT_MAX);
|
assert((unsigned)event < PIPE_EVENT_MAX);
|
||||||
|
|
||||||
pipe_events[event] = true;
|
pipe_events[event] = true;
|
||||||
(void)write(event_pipe[1], "", 1);
|
|
||||||
|
G_GNUC_UNUSED ssize_t nbytes = write(event_pipe[1], "", 1);
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||||
* http://www.musicpd.org
|
* http://www.musicpd.org
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -58,6 +58,39 @@ fifo_buffer_new(size_t size)
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
fifo_buffer_move(struct fifo_buffer *buffer);
|
||||||
|
|
||||||
|
struct fifo_buffer *
|
||||||
|
fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size)
|
||||||
|
{
|
||||||
|
if (buffer == NULL)
|
||||||
|
return new_size > 0
|
||||||
|
? fifo_buffer_new(new_size)
|
||||||
|
: NULL;
|
||||||
|
|
||||||
|
/* existing data must fit in new size */
|
||||||
|
assert(new_size >= buffer->end - buffer->start);
|
||||||
|
|
||||||
|
if (new_size == 0) {
|
||||||
|
fifo_buffer_free(buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* compress the buffer when we're shrinking and the tail of
|
||||||
|
the buffer would exceed the new size */
|
||||||
|
if (buffer->end > new_size)
|
||||||
|
fifo_buffer_move(buffer);
|
||||||
|
|
||||||
|
/* existing data must fit in new size: second check */
|
||||||
|
assert(buffer->end <= new_size);
|
||||||
|
|
||||||
|
buffer = g_realloc(buffer, sizeof(*buffer) - sizeof(buffer->buffer) +
|
||||||
|
new_size);
|
||||||
|
buffer->size = new_size;
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
fifo_buffer_free(struct fifo_buffer *buffer)
|
fifo_buffer_free(struct fifo_buffer *buffer)
|
||||||
{
|
{
|
||||||
@@ -66,6 +99,22 @@ fifo_buffer_free(struct fifo_buffer *buffer)
|
|||||||
g_free(buffer);
|
g_free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
fifo_buffer_capacity(const struct fifo_buffer *buffer)
|
||||||
|
{
|
||||||
|
assert(buffer != NULL);
|
||||||
|
|
||||||
|
return buffer->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
fifo_buffer_available(const struct fifo_buffer *buffer)
|
||||||
|
{
|
||||||
|
assert(buffer != NULL);
|
||||||
|
|
||||||
|
return buffer->end - buffer->start;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
fifo_buffer_clear(struct fifo_buffer *buffer)
|
fifo_buffer_clear(struct fifo_buffer *buffer)
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||||
* http://www.musicpd.org
|
* http://www.musicpd.org
|
||||||
*
|
*
|
||||||
* Redistribution and use in source and binary forms, with or without
|
* Redistribution and use in source and binary forms, with or without
|
||||||
@@ -56,12 +56,37 @@ struct fifo_buffer;
|
|||||||
struct fifo_buffer *
|
struct fifo_buffer *
|
||||||
fifo_buffer_new(size_t size);
|
fifo_buffer_new(size_t size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the capacity of the #fifo_buffer, while preserving existing
|
||||||
|
* data.
|
||||||
|
*
|
||||||
|
* @param buffer the old buffer, may be NULL
|
||||||
|
* @param new_size the requested new size of the #fifo_buffer; must
|
||||||
|
* not be smaller than the data which is stored in the old buffer
|
||||||
|
* @return the new buffer, may be NULL if the requested new size is 0
|
||||||
|
*/
|
||||||
|
struct fifo_buffer *
|
||||||
|
fifo_buffer_realloc(struct fifo_buffer *buffer, size_t new_size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Frees the resources consumed by this #fifo_buffer object.
|
* Frees the resources consumed by this #fifo_buffer object.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
fifo_buffer_free(struct fifo_buffer *buffer);
|
fifo_buffer_free(struct fifo_buffer *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the capacity of the buffer, i.e. the size that was passed to
|
||||||
|
* fifo_buffer_new().
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
fifo_buffer_capacity(const struct fifo_buffer *buffer);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the number of bytes currently stored in the buffer.
|
||||||
|
*/
|
||||||
|
size_t
|
||||||
|
fifo_buffer_available(const struct fifo_buffer *buffer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all data currently in this #fifo_buffer object. This does
|
* Clears all data currently in this #fifo_buffer object. This does
|
||||||
* not overwrite the actuall buffer; it just resets the internal
|
* not overwrite the actuall buffer; it just resets the internal
|
||||||
|
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
|
90
src/growing_fifo.c
Normal file
90
src/growing_fifo.c
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "growing_fifo.h"
|
||||||
|
#include "fifo_buffer.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Align buffer sizes at 8 kB boundaries. Must be a power of two.
|
||||||
|
*/
|
||||||
|
static const size_t GROWING_FIFO_ALIGN = 8192;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Align the specified size to the next #GROWING_FIFO_ALIGN boundary.
|
||||||
|
*/
|
||||||
|
static size_t
|
||||||
|
align(size_t size)
|
||||||
|
{
|
||||||
|
return ((size - 1) | (GROWING_FIFO_ALIGN - 1)) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct fifo_buffer *
|
||||||
|
growing_fifo_new(void)
|
||||||
|
{
|
||||||
|
return fifo_buffer_new(GROWING_FIFO_ALIGN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
growing_fifo_write(struct fifo_buffer **buffer_p, size_t length)
|
||||||
|
{
|
||||||
|
assert(buffer_p != NULL);
|
||||||
|
|
||||||
|
struct fifo_buffer *buffer = *buffer_p;
|
||||||
|
assert(buffer != NULL);
|
||||||
|
|
||||||
|
size_t max_length;
|
||||||
|
void *p = fifo_buffer_write(buffer, &max_length);
|
||||||
|
if (p != NULL && max_length >= length)
|
||||||
|
return p;
|
||||||
|
|
||||||
|
/* grow */
|
||||||
|
size_t new_size = fifo_buffer_available(buffer) + length;
|
||||||
|
assert(new_size > fifo_buffer_capacity(buffer));
|
||||||
|
*buffer_p = buffer = fifo_buffer_realloc(buffer, align(new_size));
|
||||||
|
|
||||||
|
/* try again */
|
||||||
|
p = fifo_buffer_write(buffer, &max_length);
|
||||||
|
assert(p != NULL);
|
||||||
|
assert(max_length >= length);
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
growing_fifo_append(struct fifo_buffer **buffer_p,
|
||||||
|
const void *data, size_t length)
|
||||||
|
{
|
||||||
|
void *p = growing_fifo_write(buffer_p, length);
|
||||||
|
memcpy(p, data, length);
|
||||||
|
fifo_buffer_append(*buffer_p, length);
|
||||||
|
}
|
73
src/growing_fifo.h
Normal file
73
src/growing_fifo.h
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2011 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the
|
||||||
|
* distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||||
|
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||||
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||||
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** \file
|
||||||
|
*
|
||||||
|
* Helper functions for our FIFO buffer library (fifo_buffer.h) that
|
||||||
|
* allows growing the buffer on demand.
|
||||||
|
*
|
||||||
|
* This library is not thread safe.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_GROWING_FIFO_H
|
||||||
|
#define MPD_GROWING_FIFO_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
struct fifo_buffer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate a new #fifo_buffer with the default size.
|
||||||
|
*/
|
||||||
|
struct fifo_buffer *
|
||||||
|
growing_fifo_new(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares writing to the buffer, see fifo_buffer_write() for
|
||||||
|
* details. The difference is that this function will automatically
|
||||||
|
* grow the buffer if it is too small.
|
||||||
|
*
|
||||||
|
* The caller is responsible for limiting the capacity of the buffer.
|
||||||
|
*
|
||||||
|
* @param length the number of bytes that will be written
|
||||||
|
* @return a pointer to the end of the buffer (will not be NULL)
|
||||||
|
*/
|
||||||
|
void *
|
||||||
|
growing_fifo_write(struct fifo_buffer **buffer_p, size_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function that combines growing_fifo_write(), memcpy(),
|
||||||
|
* fifo_buffer_append().
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
growing_fifo_append(struct fifo_buffer **buffer_p,
|
||||||
|
const void *data, size_t length);
|
||||||
|
|
||||||
|
#endif
|
@@ -42,6 +42,13 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "input_curl"
|
#define G_LOG_DOMAIN "input_curl"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not buffer more than this number of bytes. It should be a
|
||||||
|
* reasonable limit that doesn't make low-end machines suffer too
|
||||||
|
* much, but doesn't cause stuttering on high-latency lines.
|
||||||
|
*/
|
||||||
|
static const size_t CURL_MAX_BUFFERED = 512 * 1024;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Buffers created by input_curl_writefunction().
|
* Buffers created by input_curl_writefunction().
|
||||||
*/
|
*/
|
||||||
@@ -144,6 +151,25 @@ input_curl_finish(void)
|
|||||||
curl_global_cleanup();
|
curl_global_cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the total sizes of all buffers, including portions that
|
||||||
|
* have already been consumed.
|
||||||
|
*/
|
||||||
|
G_GNUC_PURE
|
||||||
|
static size_t
|
||||||
|
curl_total_buffer_size(const struct input_curl *c)
|
||||||
|
{
|
||||||
|
size_t total = 0;
|
||||||
|
|
||||||
|
for (GList *i = g_queue_peek_head_link(c->buffers);
|
||||||
|
i != NULL; i = g_list_next(i)) {
|
||||||
|
struct buffer *buffer = i->data;
|
||||||
|
total += buffer->size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
|
buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
|
||||||
{
|
{
|
||||||
@@ -151,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -473,6 +499,10 @@ static int
|
|||||||
input_curl_buffer(struct input_stream *is, GError **error_r)
|
input_curl_buffer(struct input_stream *is, GError **error_r)
|
||||||
{
|
{
|
||||||
struct input_curl *c = (struct input_curl *)is;
|
struct input_curl *c = (struct input_curl *)is;
|
||||||
|
|
||||||
|
if (curl_total_buffer_size(c) >= CURL_MAX_BUFFERED)
|
||||||
|
return 0;
|
||||||
|
|
||||||
CURLMcode mcode;
|
CURLMcode mcode;
|
||||||
int running_handles;
|
int running_handles;
|
||||||
bool ret;
|
bool ret;
|
||||||
@@ -650,6 +680,9 @@ input_curl_easy_init(struct input_curl *c, GError **error_r)
|
|||||||
curl_easy_setopt(c->easy, CURLOPT_MAXREDIRS, 5);
|
curl_easy_setopt(c->easy, CURLOPT_MAXREDIRS, 5);
|
||||||
curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true);
|
curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true);
|
||||||
curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error);
|
curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error);
|
||||||
|
curl_easy_setopt(c->easy, CURLOPT_NOPROGRESS, 1l);
|
||||||
|
curl_easy_setopt(c->easy, CURLOPT_NOSIGNAL, 1l);
|
||||||
|
curl_easy_setopt(c->easy, CURLOPT_CONNECTTIMEOUT, 10l);
|
||||||
|
|
||||||
if (proxy != NULL)
|
if (proxy != NULL)
|
||||||
curl_easy_setopt(c->easy, CURLOPT_PROXY, proxy);
|
curl_easy_setopt(c->easy, CURLOPT_PROXY, proxy);
|
||||||
|
@@ -32,10 +32,18 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "input_ffmpeg"
|
#define G_LOG_DOMAIN "input_ffmpeg"
|
||||||
|
|
||||||
|
#ifndef AV_VERSION_INT
|
||||||
|
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
|
||||||
|
#endif
|
||||||
|
|
||||||
struct input_ffmpeg {
|
struct input_ffmpeg {
|
||||||
struct input_stream base;
|
struct input_stream base;
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
|
||||||
|
AVIOContext *h;
|
||||||
|
#else
|
||||||
URLContext *h;
|
URLContext *h;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool eof;
|
bool eof;
|
||||||
};
|
};
|
||||||
@@ -46,6 +54,17 @@ ffmpeg_quark(void)
|
|||||||
return g_quark_from_static_string("ffmpeg");
|
return g_quark_from_static_string("ffmpeg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
input_ffmpeg_supported(void)
|
||||||
|
{
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
|
||||||
|
void *opaque = NULL;
|
||||||
|
return avio_enum_protocols(&opaque, 0) != NULL;
|
||||||
|
#else
|
||||||
|
return av_protocol_next(NULL) != NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param,
|
input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param,
|
||||||
G_GNUC_UNUSED GError **error_r)
|
G_GNUC_UNUSED GError **error_r)
|
||||||
@@ -54,7 +73,7 @@ input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param,
|
|||||||
|
|
||||||
#if LIBAVFORMAT_VERSION_MAJOR >= 52
|
#if LIBAVFORMAT_VERSION_MAJOR >= 52
|
||||||
/* disable this plugin if there's no registered protocol */
|
/* disable this plugin if there's no registered protocol */
|
||||||
if (av_protocol_next(NULL) == NULL) {
|
if (!input_ffmpeg_supported()) {
|
||||||
g_set_error(error_r, ffmpeg_quark(), 0,
|
g_set_error(error_r, ffmpeg_quark(), 0,
|
||||||
"No protocol");
|
"No protocol");
|
||||||
return false;
|
return false;
|
||||||
@@ -80,7 +99,11 @@ input_ffmpeg_open(const char *uri, GError **error_r)
|
|||||||
i = g_new(struct input_ffmpeg, 1);
|
i = g_new(struct input_ffmpeg, 1);
|
||||||
input_stream_init(&i->base, &input_plugin_ffmpeg, uri);
|
input_stream_init(&i->base, &input_plugin_ffmpeg, uri);
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
|
||||||
|
int ret = avio_open(&i->h, uri, AVIO_FLAG_READ);
|
||||||
|
#else
|
||||||
int ret = url_open(&i->h, uri, URL_RDONLY);
|
int ret = url_open(&i->h, uri, URL_RDONLY);
|
||||||
|
#endif
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
g_free(i);
|
g_free(i);
|
||||||
g_set_error(error_r, ffmpeg_quark(), ret,
|
g_set_error(error_r, ffmpeg_quark(), ret,
|
||||||
@@ -91,8 +114,13 @@ input_ffmpeg_open(const char *uri, GError **error_r)
|
|||||||
i->eof = false;
|
i->eof = false;
|
||||||
|
|
||||||
i->base.ready = true;
|
i->base.ready = true;
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
|
||||||
|
i->base.seekable = (i->h->seekable & AVIO_SEEKABLE_NORMAL) != 0;
|
||||||
|
i->base.size = avio_size(i->h);
|
||||||
|
#else
|
||||||
i->base.seekable = !i->h->is_streamed;
|
i->base.seekable = !i->h->is_streamed;
|
||||||
i->base.size = url_filesize(i->h);
|
i->base.size = url_filesize(i->h);
|
||||||
|
#endif
|
||||||
|
|
||||||
/* hack to make MPD select the "ffmpeg" decoder plugin - since
|
/* hack to make MPD select the "ffmpeg" decoder plugin - since
|
||||||
avio.h doesn't tell us the MIME type of the resource, we
|
avio.h doesn't tell us the MIME type of the resource, we
|
||||||
@@ -109,7 +137,11 @@ input_ffmpeg_read(struct input_stream *is, void *ptr, size_t size,
|
|||||||
{
|
{
|
||||||
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
|
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
|
||||||
|
int ret = avio_read(i->h, ptr, size);
|
||||||
|
#else
|
||||||
int ret = url_read(i->h, ptr, size);
|
int ret = url_read(i->h, ptr, size);
|
||||||
|
#endif
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
g_set_error(error_r, ffmpeg_quark(), 0,
|
g_set_error(error_r, ffmpeg_quark(), 0,
|
||||||
@@ -128,7 +160,11 @@ input_ffmpeg_close(struct input_stream *is)
|
|||||||
{
|
{
|
||||||
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
|
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
|
||||||
|
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
|
||||||
|
avio_close(i->h);
|
||||||
|
#else
|
||||||
url_close(i->h);
|
url_close(i->h);
|
||||||
|
#endif
|
||||||
input_stream_deinit(&i->base);
|
input_stream_deinit(&i->base);
|
||||||
g_free(i);
|
g_free(i);
|
||||||
}
|
}
|
||||||
@@ -146,7 +182,11 @@ input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence,
|
|||||||
G_GNUC_UNUSED GError **error_r)
|
G_GNUC_UNUSED GError **error_r)
|
||||||
{
|
{
|
||||||
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
|
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
|
||||||
|
int64_t ret = avio_seek(i->h, offset, whence);
|
||||||
|
#else
|
||||||
int64_t ret = url_seek(i->h, offset, whence);
|
int64_t ret = url_seek(i->h, offset, whence);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (ret >= 0) {
|
if (ret >= 0) {
|
||||||
i->eof = false;
|
i->eof = false;
|
||||||
|
@@ -81,14 +81,16 @@ copy_attributes(struct input_rewind *r)
|
|||||||
const struct input_stream *src = r->input;
|
const struct input_stream *src = r->input;
|
||||||
|
|
||||||
assert(dest != src);
|
assert(dest != src);
|
||||||
assert(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);
|
||||||
}
|
}
|
||||||
|
@@ -139,8 +139,8 @@ log_init_file(const char *path, unsigned line)
|
|||||||
out_filename = path;
|
out_filename = path;
|
||||||
out_fd = open_log_file();
|
out_fd = open_log_file();
|
||||||
if (out_fd < 0)
|
if (out_fd < 0)
|
||||||
MPD_ERROR("problem opening log file \"%s\" (config line %u) "
|
MPD_ERROR("failed to open log file \"%s\" (config line %u): %s",
|
||||||
"for writing\n", path, line);
|
path, line, g_strerror(errno));
|
||||||
|
|
||||||
g_log_set_default_handler(file_log_func, NULL);
|
g_log_set_default_handler(file_log_func, NULL);
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,6 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
#define WINVER 0x0501
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
static int service_argc;
|
static int service_argc;
|
||||||
|
46
src/mapper.c
46
src/mapper.c
@@ -31,6 +31,10 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
static char *music_dir;
|
static char *music_dir;
|
||||||
static size_t music_dir_length;
|
static size_t music_dir_length;
|
||||||
@@ -51,25 +55,51 @@ strdup_chop_slash(const char *path_fs)
|
|||||||
return g_strndup(path_fs, length);
|
return g_strndup(path_fs, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_directory(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if (stat(path, &st) < 0) {
|
||||||
|
g_warning("Failed to stat directory \"%s\": %s",
|
||||||
|
path, g_strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!S_ISDIR(st.st_mode)) {
|
||||||
|
g_warning("Not a directory: %s", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
char *x = g_build_filename(path, ".", NULL);
|
||||||
|
if (stat(x, &st) < 0 && errno == EACCES)
|
||||||
|
g_warning("No permission to traverse (\"execute\") directory: %s",
|
||||||
|
path);
|
||||||
|
g_free(x);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DIR *dir = opendir(path);
|
||||||
|
if (dir == NULL && errno == EACCES)
|
||||||
|
g_warning("No permission to read directory: %s", path);
|
||||||
|
else
|
||||||
|
closedir(dir);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mapper_set_music_dir(const char *path)
|
mapper_set_music_dir(const char *path)
|
||||||
{
|
{
|
||||||
|
check_directory(path);
|
||||||
|
|
||||||
music_dir = strdup_chop_slash(path);
|
music_dir = strdup_chop_slash(path);
|
||||||
music_dir_length = strlen(music_dir);
|
music_dir_length = strlen(music_dir);
|
||||||
|
|
||||||
if (!g_file_test(music_dir, G_FILE_TEST_IS_DIR))
|
|
||||||
g_warning("music directory is not a directory: \"%s\"",
|
|
||||||
music_dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
mapper_set_playlist_dir(const char *path)
|
mapper_set_playlist_dir(const char *path)
|
||||||
{
|
{
|
||||||
playlist_dir = g_strdup(path);
|
check_directory(path);
|
||||||
|
|
||||||
if (!g_file_test(playlist_dir, G_FILE_TEST_IS_DIR))
|
playlist_dir = g_strdup(path);
|
||||||
g_warning("playlist directory is not a directory: \"%s\"",
|
|
||||||
playlist_dir);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void mapper_init(const char *_music_dir, const char *_playlist_dir)
|
void mapper_init(const char *_music_dir, const char *_playlist_dir)
|
||||||
|
@@ -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
|
||||||
@@ -508,6 +511,14 @@ configure_hw:
|
|||||||
g_debug("buffer_size=%u period_size=%u",
|
g_debug("buffer_size=%u period_size=%u",
|
||||||
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
(unsigned)alsa_buffer_size, (unsigned)alsa_period_size);
|
||||||
|
|
||||||
|
if (alsa_period_size == 0)
|
||||||
|
/* this works around a SIGFPE bug that occurred when
|
||||||
|
an ALSA driver indicated period_size==0; this
|
||||||
|
caused a division by zero in alsa_play(). By using
|
||||||
|
the fallback "1", we make sure that this won't
|
||||||
|
happen again. */
|
||||||
|
alsa_period_size = 1;
|
||||||
|
|
||||||
ad->period_frames = alsa_period_size;
|
ad->period_frames = alsa_period_size;
|
||||||
ad->period_position = 0;
|
ad->period_position = 0;
|
||||||
|
|
||||||
|
@@ -26,6 +26,9 @@
|
|||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "ao"
|
#define G_LOG_DOMAIN "ao"
|
||||||
|
|
||||||
|
/* An ao_sample_format, with all fields set to zero: */
|
||||||
|
static const ao_sample_format OUR_AO_FORMAT_INITIALIZER;
|
||||||
|
|
||||||
static unsigned ao_output_ref;
|
static unsigned ao_output_ref;
|
||||||
|
|
||||||
struct ao_data {
|
struct ao_data {
|
||||||
@@ -103,12 +106,14 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
|||||||
g_set_error(error, ao_output_quark(), 0,
|
g_set_error(error, ao_output_quark(), 0,
|
||||||
"\"%s\" is not a valid ao driver",
|
"\"%s\" is not a valid ao driver",
|
||||||
value);
|
value);
|
||||||
|
g_free(ad);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((ai = ao_driver_info(ad->driver)) == NULL) {
|
if ((ai = ao_driver_info(ad->driver)) == NULL) {
|
||||||
g_set_error(error, ao_output_quark(), 0,
|
g_set_error(error, ao_output_quark(), 0,
|
||||||
"problems getting driver info");
|
"problems getting driver info");
|
||||||
|
g_free(ad);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +131,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
|||||||
g_set_error(error, ao_output_quark(), 0,
|
g_set_error(error, ao_output_quark(), 0,
|
||||||
"problems parsing options \"%s\"",
|
"problems parsing options \"%s\"",
|
||||||
options[i]);
|
options[i]);
|
||||||
|
g_free(ad);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,7 +173,7 @@ static bool
|
|||||||
ao_output_open(void *data, struct audio_format *audio_format,
|
ao_output_open(void *data, struct audio_format *audio_format,
|
||||||
GError **error)
|
GError **error)
|
||||||
{
|
{
|
||||||
ao_sample_format format;
|
ao_sample_format format = OUR_AO_FORMAT_INITIALIZER;
|
||||||
struct ao_data *ad = (struct ao_data *)data;
|
struct ao_data *ad = (struct ao_data *)data;
|
||||||
|
|
||||||
switch (audio_format->format) {
|
switch (audio_format->format) {
|
||||||
|
@@ -24,11 +24,15 @@
|
|||||||
#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>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#undef G_LOG_DOMAIN
|
||||||
|
#define G_LOG_DOMAIN "httpd_output"
|
||||||
|
|
||||||
struct httpd_client {
|
struct httpd_client {
|
||||||
/**
|
/**
|
||||||
* The httpd output object this client is connected to.
|
* The httpd output object this client is connected to.
|
||||||
@@ -140,6 +144,8 @@ httpd_client_unref_page(gpointer data, G_GNUC_UNUSED gpointer user_data)
|
|||||||
void
|
void
|
||||||
httpd_client_free(struct httpd_client *client)
|
httpd_client_free(struct httpd_client *client)
|
||||||
{
|
{
|
||||||
|
assert(client != NULL);
|
||||||
|
|
||||||
if (client->state == RESPONSE) {
|
if (client->state == RESPONSE) {
|
||||||
if (client->write_source_id != 0)
|
if (client->write_source_id != 0)
|
||||||
g_source_remove(client->write_source_id);
|
g_source_remove(client->write_source_id);
|
||||||
@@ -166,6 +172,8 @@ httpd_client_free(struct httpd_client *client)
|
|||||||
static void
|
static void
|
||||||
httpd_client_close(struct httpd_client *client)
|
httpd_client_close(struct httpd_client *client)
|
||||||
{
|
{
|
||||||
|
assert(client != NULL);
|
||||||
|
|
||||||
httpd_output_remove_client(client->httpd, client);
|
httpd_output_remove_client(client->httpd, client);
|
||||||
httpd_client_free(client);
|
httpd_client_free(client);
|
||||||
}
|
}
|
||||||
@@ -176,6 +184,9 @@ httpd_client_close(struct httpd_client *client)
|
|||||||
static void
|
static void
|
||||||
httpd_client_begin_response(struct httpd_client *client)
|
httpd_client_begin_response(struct httpd_client *client)
|
||||||
{
|
{
|
||||||
|
assert(client != NULL);
|
||||||
|
assert(client->state != RESPONSE);
|
||||||
|
|
||||||
client->state = RESPONSE;
|
client->state = RESPONSE;
|
||||||
client->write_source_id = 0;
|
client->write_source_id = 0;
|
||||||
client->pages = g_queue_new();
|
client->pages = g_queue_new();
|
||||||
@@ -236,6 +247,9 @@ httpd_client_handle_line(struct httpd_client *client, const char *line)
|
|||||||
static char *
|
static char *
|
||||||
httpd_client_read_line(struct httpd_client *client)
|
httpd_client_read_line(struct httpd_client *client)
|
||||||
{
|
{
|
||||||
|
assert(client != NULL);
|
||||||
|
assert(client->state != RESPONSE);
|
||||||
|
|
||||||
const char *p, *newline;
|
const char *p, *newline;
|
||||||
size_t length;
|
size_t length;
|
||||||
char *line;
|
char *line;
|
||||||
@@ -268,6 +282,7 @@ httpd_client_send_response(struct httpd_client *client)
|
|||||||
GIOStatus status;
|
GIOStatus status;
|
||||||
gsize bytes_written;
|
gsize bytes_written;
|
||||||
|
|
||||||
|
assert(client != NULL);
|
||||||
assert(client->state == RESPONSE);
|
assert(client->state == RESPONSE);
|
||||||
|
|
||||||
if (!client->metadata_requested) {
|
if (!client->metadata_requested) {
|
||||||
@@ -331,14 +346,19 @@ httpd_client_send_response(struct httpd_client *client)
|
|||||||
static bool
|
static bool
|
||||||
httpd_client_received(struct httpd_client *client)
|
httpd_client_received(struct httpd_client *client)
|
||||||
{
|
{
|
||||||
|
assert(client != NULL);
|
||||||
|
assert(client->state != RESPONSE);
|
||||||
|
|
||||||
char *line;
|
char *line;
|
||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
while ((line = httpd_client_read_line(client)) != NULL) {
|
while ((line = httpd_client_read_line(client)) != NULL) {
|
||||||
success = httpd_client_handle_line(client, line);
|
success = httpd_client_handle_line(client, line);
|
||||||
g_free(line);
|
g_free(line);
|
||||||
if (!success)
|
if (!success) {
|
||||||
|
assert(client->state != RESPONSE);
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (client->state == RESPONSE) {
|
if (client->state == RESPONSE) {
|
||||||
if (!fifo_buffer_is_empty(client->input)) {
|
if (!fifo_buffer_is_empty(client->input)) {
|
||||||
@@ -367,7 +387,14 @@ httpd_client_read(struct httpd_client *client)
|
|||||||
if (client->state == RESPONSE) {
|
if (client->state == RESPONSE) {
|
||||||
/* the client has already sent the request, and he
|
/* the client has already sent the request, and he
|
||||||
must not send more */
|
must not send more */
|
||||||
|
char buffer[1];
|
||||||
|
|
||||||
|
status = g_io_channel_read_chars(client->channel, buffer,
|
||||||
|
sizeof(buffer), &bytes_read,
|
||||||
|
NULL);
|
||||||
|
if (status == G_IO_STATUS_NORMAL)
|
||||||
g_warning("unexpected input from client");
|
g_warning("unexpected input from client");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,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);
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
#ifdef HAVE_LIBWRAP
|
#ifdef HAVE_LIBWRAP
|
||||||
|
#include <sys/socket.h> /* needed for AF_UNIX */
|
||||||
#include <tcpd.h>
|
#include <tcpd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -102,6 +103,7 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
|||||||
if (encoder_plugin == NULL) {
|
if (encoder_plugin == NULL) {
|
||||||
g_set_error(error, httpd_output_quark(), 0,
|
g_set_error(error, httpd_output_quark(), 0,
|
||||||
"No such encoder: %s", encoder_name);
|
"No such encoder: %s", encoder_name);
|
||||||
|
g_free(httpd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +125,7 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
|||||||
|
|
||||||
/* initialize metadata */
|
/* initialize metadata */
|
||||||
httpd->metadata = NULL;
|
httpd->metadata = NULL;
|
||||||
|
httpd->unflushed_input = 0;
|
||||||
|
|
||||||
/* initialize encoder */
|
/* initialize encoder */
|
||||||
|
|
||||||
@@ -198,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;
|
||||||
}
|
}
|
||||||
@@ -219,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));
|
||||||
}
|
}
|
||||||
@@ -490,7 +493,8 @@ httpd_output_pause(void *data)
|
|||||||
|
|
||||||
if (has_clients) {
|
if (has_clients) {
|
||||||
static const char silence[1020];
|
static const char silence[1020];
|
||||||
return httpd_output_play(data, silence, sizeof(silence), NULL);
|
return httpd_output_play(data, silence, sizeof(silence),
|
||||||
|
NULL) > 0;
|
||||||
} else {
|
} else {
|
||||||
g_usleep(100000);
|
g_usleep(100000);
|
||||||
return true;
|
return true;
|
||||||
@@ -519,7 +523,7 @@ httpd_output_tag(void *data, const struct tag *tag)
|
|||||||
|
|
||||||
/* flush the current stream, and end it */
|
/* flush the current stream, and end it */
|
||||||
|
|
||||||
encoder_flush(httpd->encoder, NULL);
|
encoder_pre_tag(httpd->encoder, NULL);
|
||||||
httpd_output_encoder_to_clients(httpd);
|
httpd_output_encoder_to_clients(httpd);
|
||||||
|
|
||||||
/* send the tag to the encoder - which starts a new
|
/* send the tag to the encoder - which starts a new
|
||||||
|
@@ -40,7 +40,7 @@ enum {
|
|||||||
MAX_PORTS = 16,
|
MAX_PORTS = 16,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const size_t sample_size = sizeof(jack_default_audio_sample_t);
|
static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t);
|
||||||
|
|
||||||
struct jack_data {
|
struct jack_data {
|
||||||
/**
|
/**
|
||||||
@@ -103,9 +103,9 @@ mpd_jack_available(const struct jack_data *jd)
|
|||||||
min = current;
|
min = current;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(min % sample_size == 0);
|
assert(min % jack_sample_size == 0);
|
||||||
|
|
||||||
return min / sample_size;
|
return min / jack_sample_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@@ -123,7 +123,7 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
|
|||||||
const jack_nframes_t available = mpd_jack_available(jd);
|
const jack_nframes_t available = mpd_jack_available(jd);
|
||||||
for (unsigned i = 0; i < jd->audio_format.channels; ++i)
|
for (unsigned i = 0; i < jd->audio_format.channels; ++i)
|
||||||
jack_ringbuffer_read_advance(jd->ringbuffer[i],
|
jack_ringbuffer_read_advance(jd->ringbuffer[i],
|
||||||
available * sample_size);
|
available * jack_sample_size);
|
||||||
|
|
||||||
/* generate silence while MPD is paused */
|
/* generate silence while MPD is paused */
|
||||||
|
|
||||||
@@ -143,8 +143,15 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
|
|||||||
|
|
||||||
for (unsigned i = 0; i < jd->audio_format.channels; ++i) {
|
for (unsigned i = 0; i < jd->audio_format.channels; ++i) {
|
||||||
out = jack_port_get_buffer(jd->ports[i], nframes);
|
out = jack_port_get_buffer(jd->ports[i], nframes);
|
||||||
|
if (out == NULL)
|
||||||
|
/* workaround for libjack1 bug: if the server
|
||||||
|
connection fails, the process callback is
|
||||||
|
invoked anyway, but unable to get a
|
||||||
|
buffer */
|
||||||
|
continue;
|
||||||
|
|
||||||
jack_ringbuffer_read(jd->ringbuffer[i],
|
jack_ringbuffer_read(jd->ringbuffer[i],
|
||||||
(char *)out, available * sample_size);
|
(char *)out, available * jack_sample_size);
|
||||||
|
|
||||||
for (jack_nframes_t f = available; f < nframes; ++f)
|
for (jack_nframes_t f = available; f < nframes; ++f)
|
||||||
/* ringbuffer underrun, fill with silence */
|
/* ringbuffer underrun, fill with silence */
|
||||||
@@ -156,6 +163,12 @@ mpd_jack_process(jack_nframes_t nframes, void *arg)
|
|||||||
for (unsigned i = jd->audio_format.channels;
|
for (unsigned i = jd->audio_format.channels;
|
||||||
i < jd->num_source_ports; ++i) {
|
i < jd->num_source_ports; ++i) {
|
||||||
out = jack_port_get_buffer(jd->ports[i], nframes);
|
out = jack_port_get_buffer(jd->ports[i], nframes);
|
||||||
|
if (out == NULL)
|
||||||
|
/* workaround for libjack1 bug: if the server
|
||||||
|
connection fails, the process callback is
|
||||||
|
invoked anyway, but unable to get a
|
||||||
|
buffer */
|
||||||
|
continue;
|
||||||
|
|
||||||
for (jack_nframes_t f = 0; f < nframes; ++f)
|
for (jack_nframes_t f = 0; f < nframes; ++f)
|
||||||
out[f] = 0.0;
|
out[f] = 0.0;
|
||||||
@@ -563,6 +576,9 @@ mpd_jack_open(void *data, struct audio_format *audio_format, GError **error_r)
|
|||||||
|
|
||||||
jd->pause = false;
|
jd->pause = false;
|
||||||
|
|
||||||
|
if (jd->client != NULL && jd->shutdown)
|
||||||
|
mpd_jack_disconnect(jd);
|
||||||
|
|
||||||
if (jd->client == NULL && !mpd_jack_connect(jd, error_r))
|
if (jd->client == NULL && !mpd_jack_connect(jd, error_r))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -675,7 +691,7 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r)
|
|||||||
space = space1;
|
space = space1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (space >= frame_size)
|
if (space >= jack_sample_size)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* XXX do something more intelligent to
|
/* XXX do something more intelligent to
|
||||||
@@ -683,7 +699,7 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r)
|
|||||||
g_usleep(1000);
|
g_usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
space /= sample_size;
|
space /= jack_sample_size;
|
||||||
if (space < size)
|
if (space < size)
|
||||||
size = space;
|
size = space;
|
||||||
|
|
||||||
|
@@ -58,32 +58,26 @@ openal_output_quark(void)
|
|||||||
static ALenum
|
static ALenum
|
||||||
openal_audio_format(struct audio_format *audio_format)
|
openal_audio_format(struct audio_format *audio_format)
|
||||||
{
|
{
|
||||||
|
/* note: cannot map SAMPLE_FORMAT_S8 to AL_FORMAT_STEREO8 or
|
||||||
|
AL_FORMAT_MONO8 since OpenAL expects unsigned 8 bit
|
||||||
|
samples, while MPD uses signed samples */
|
||||||
|
|
||||||
switch (audio_format->format) {
|
switch (audio_format->format) {
|
||||||
case SAMPLE_FORMAT_S16:
|
case SAMPLE_FORMAT_S16:
|
||||||
if (audio_format->channels == 2)
|
if (audio_format->channels == 2)
|
||||||
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;
|
|
||||||
|
|
||||||
case SAMPLE_FORMAT_S8:
|
/* fall back to mono */
|
||||||
if (audio_format->channels == 2)
|
audio_format->channels = 1;
|
||||||
return AL_FORMAT_STEREO8;
|
return openal_audio_format(audio_format);
|
||||||
if (audio_format->channels == 1)
|
|
||||||
return AL_FORMAT_MONO8;
|
|
||||||
break;
|
|
||||||
|
|
||||||
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
|
||||||
|
@@ -41,6 +41,15 @@
|
|||||||
# include <sys/soundcard.h>
|
# include <sys/soundcard.h>
|
||||||
#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
|
#endif /* !(defined(__OpenBSD__) || defined(__NetBSD__) */
|
||||||
|
|
||||||
|
/* We got bug reports from FreeBSD users who said that the two 24 bit
|
||||||
|
formats generate white noise on FreeBSD, but 32 bit works. This is
|
||||||
|
a workaround until we know what exactly is expected by the kernel
|
||||||
|
audio drivers. */
|
||||||
|
#ifndef __linux__
|
||||||
|
#undef AFMT_S24_PACKED
|
||||||
|
#undef AFMT_S24_NE
|
||||||
|
#endif
|
||||||
|
|
||||||
struct oss_data {
|
struct oss_data {
|
||||||
int fd;
|
int fd;
|
||||||
const char *device;
|
const char *device;
|
||||||
@@ -461,6 +470,12 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
audio_format->format = mpd_format;
|
audio_format->format = mpd_format;
|
||||||
|
|
||||||
|
#ifdef AFMT_S24_PACKED
|
||||||
|
if (oss_format == AFMT_S24_PACKED)
|
||||||
|
audio_format->reverse_endian =
|
||||||
|
G_BYTE_ORDER != G_LITTLE_ENDIAN;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ERROR:
|
case ERROR:
|
||||||
@@ -502,6 +517,12 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
audio_format->format = mpd_format;
|
audio_format->format = mpd_format;
|
||||||
|
|
||||||
|
#ifdef AFMT_S24_PACKED
|
||||||
|
if (oss_format == AFMT_S24_PACKED)
|
||||||
|
audio_format->reverse_endian =
|
||||||
|
G_BYTE_ORDER != G_LITTLE_ENDIAN;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case ERROR:
|
case ERROR:
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "output_api.h"
|
#include "output_api.h"
|
||||||
|
#include "fifo_buffer.h"
|
||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
#include <AudioUnit/AudioUnit.h>
|
#include <AudioUnit/AudioUnit.h>
|
||||||
@@ -31,10 +32,8 @@ struct osx_output {
|
|||||||
AudioUnit au;
|
AudioUnit au;
|
||||||
GMutex *mutex;
|
GMutex *mutex;
|
||||||
GCond *condition;
|
GCond *condition;
|
||||||
char *buffer;
|
|
||||||
size_t buffer_size;
|
struct fifo_buffer *buffer;
|
||||||
size_t pos;
|
|
||||||
size_t len;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,11 +63,6 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
|||||||
oo->mutex = g_mutex_new();
|
oo->mutex = g_mutex_new();
|
||||||
oo->condition = g_cond_new();
|
oo->condition = g_cond_new();
|
||||||
|
|
||||||
oo->pos = 0;
|
|
||||||
oo->len = 0;
|
|
||||||
oo->buffer = NULL;
|
|
||||||
oo->buffer_size = 0;
|
|
||||||
|
|
||||||
return oo;
|
return oo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +70,6 @@ static void osx_output_finish(void *data)
|
|||||||
{
|
{
|
||||||
struct osx_output *od = data;
|
struct osx_output *od = data;
|
||||||
|
|
||||||
g_free(od->buffer);
|
|
||||||
g_mutex_free(od->mutex);
|
g_mutex_free(od->mutex);
|
||||||
g_cond_free(od->condition);
|
g_cond_free(od->condition);
|
||||||
g_free(od);
|
g_free(od);
|
||||||
@@ -87,7 +80,7 @@ static void osx_output_cancel(void *data)
|
|||||||
struct osx_output *od = data;
|
struct osx_output *od = data;
|
||||||
|
|
||||||
g_mutex_lock(od->mutex);
|
g_mutex_lock(od->mutex);
|
||||||
od->len = 0;
|
fifo_buffer_clear(od->buffer);
|
||||||
g_mutex_unlock(od->mutex);
|
g_mutex_unlock(od->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,15 +88,11 @@ static void osx_output_close(void *data)
|
|||||||
{
|
{
|
||||||
struct osx_output *od = data;
|
struct osx_output *od = data;
|
||||||
|
|
||||||
g_mutex_lock(od->mutex);
|
|
||||||
while (od->len) {
|
|
||||||
g_cond_wait(od->condition, od->mutex);
|
|
||||||
}
|
|
||||||
g_mutex_unlock(od->mutex);
|
|
||||||
|
|
||||||
AudioOutputUnitStop(od->au);
|
AudioOutputUnitStop(od->au);
|
||||||
AudioUnitUninitialize(od->au);
|
AudioUnitUninitialize(od->au);
|
||||||
CloseComponent(od->au);
|
CloseComponent(od->au);
|
||||||
|
|
||||||
|
fifo_buffer_free(od->buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static OSStatus
|
static OSStatus
|
||||||
@@ -117,40 +106,29 @@ osx_render(void *vdata,
|
|||||||
struct osx_output *od = (struct osx_output *) vdata;
|
struct osx_output *od = (struct osx_output *) vdata;
|
||||||
AudioBuffer *buffer = &buffer_list->mBuffers[0];
|
AudioBuffer *buffer = &buffer_list->mBuffers[0];
|
||||||
size_t buffer_size = buffer->mDataByteSize;
|
size_t buffer_size = buffer->mDataByteSize;
|
||||||
size_t bytes_to_copy;
|
|
||||||
size_t trailer_length;
|
assert(od->buffer != NULL);
|
||||||
size_t dest_pos = 0;
|
|
||||||
|
|
||||||
g_mutex_lock(od->mutex);
|
g_mutex_lock(od->mutex);
|
||||||
|
|
||||||
bytes_to_copy = MIN(od->len, buffer_size);
|
size_t nbytes;
|
||||||
buffer_size = bytes_to_copy;
|
const void *src = fifo_buffer_read(od->buffer, &nbytes);
|
||||||
od->len -= bytes_to_copy;
|
|
||||||
|
|
||||||
trailer_length = od->buffer_size - od->pos;
|
if (src != NULL) {
|
||||||
if (bytes_to_copy > trailer_length) {
|
if (nbytes > buffer_size)
|
||||||
memcpy((unsigned char*)buffer->mData + dest_pos,
|
nbytes = buffer_size;
|
||||||
od->buffer + od->pos, trailer_length);
|
|
||||||
od->pos = 0;
|
|
||||||
dest_pos += trailer_length;
|
|
||||||
bytes_to_copy -= trailer_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy((unsigned char*)buffer->mData + dest_pos,
|
memcpy(buffer->mData, src, nbytes);
|
||||||
od->buffer + od->pos, bytes_to_copy);
|
fifo_buffer_consume(od->buffer, nbytes);
|
||||||
od->pos += bytes_to_copy;
|
} else
|
||||||
|
nbytes = 0;
|
||||||
|
|
||||||
if (od->pos >= od->buffer_size)
|
|
||||||
od->pos = 0;
|
|
||||||
|
|
||||||
g_mutex_unlock(od->mutex);
|
|
||||||
g_cond_signal(od->condition);
|
g_cond_signal(od->condition);
|
||||||
|
g_mutex_unlock(od->mutex);
|
||||||
|
|
||||||
buffer->mDataByteSize = buffer_size;
|
if (nbytes < buffer_size)
|
||||||
|
memset((unsigned char*)buffer->mData + nbytes, 0,
|
||||||
if (!buffer_size) {
|
buffer_size - nbytes);
|
||||||
g_usleep(1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -214,15 +192,6 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
|
|||||||
stream_description.mSampleRate = audio_format->sample_rate;
|
stream_description.mSampleRate = audio_format->sample_rate;
|
||||||
stream_description.mFormatID = kAudioFormatLinearPCM;
|
stream_description.mFormatID = kAudioFormatLinearPCM;
|
||||||
stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
stream_description.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger;
|
||||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
|
||||||
stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
stream_description.mBytesPerPacket =
|
|
||||||
audio_format_frame_size(audio_format);
|
|
||||||
stream_description.mFramesPerPacket = 1;
|
|
||||||
stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
|
|
||||||
stream_description.mChannelsPerFrame = audio_format->channels;
|
|
||||||
|
|
||||||
switch (audio_format->format) {
|
switch (audio_format->format) {
|
||||||
case SAMPLE_FORMAT_S8:
|
case SAMPLE_FORMAT_S8:
|
||||||
@@ -239,6 +208,16 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||||
|
stream_description.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
stream_description.mBytesPerPacket =
|
||||||
|
audio_format_frame_size(audio_format);
|
||||||
|
stream_description.mFramesPerPacket = 1;
|
||||||
|
stream_description.mBytesPerFrame = stream_description.mBytesPerPacket;
|
||||||
|
stream_description.mChannelsPerFrame = audio_format->channels;
|
||||||
|
|
||||||
result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
|
result = AudioUnitSetProperty(od->au, kAudioUnitProperty_StreamFormat,
|
||||||
kAudioUnitScope_Input, 0,
|
kAudioUnitScope_Input, 0,
|
||||||
&stream_description,
|
&stream_description,
|
||||||
@@ -252,15 +231,12 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* create a buffer of 1s */
|
/* create a buffer of 1s */
|
||||||
od->buffer_size = (audio_format->sample_rate) *
|
od->buffer = fifo_buffer_new(audio_format->sample_rate *
|
||||||
audio_format_frame_size(audio_format);
|
audio_format_frame_size(audio_format));
|
||||||
od->buffer = g_realloc(od->buffer, od->buffer_size);
|
|
||||||
|
|
||||||
od->pos = 0;
|
|
||||||
od->len = 0;
|
|
||||||
|
|
||||||
status = AudioOutputUnitStart(od->au);
|
status = AudioOutputUnitStart(od->au);
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
|
fifo_buffer_free(od->buffer);
|
||||||
g_set_error(error, osx_output_quark(), 0,
|
g_set_error(error, osx_output_quark(), 0,
|
||||||
"unable to start audio output: %s",
|
"unable to start audio output: %s",
|
||||||
GetMacOSStatusCommentString(status));
|
GetMacOSStatusCommentString(status));
|
||||||
@@ -275,33 +251,30 @@ osx_output_play(void *data, const void *chunk, size_t size,
|
|||||||
G_GNUC_UNUSED GError **error)
|
G_GNUC_UNUSED GError **error)
|
||||||
{
|
{
|
||||||
struct osx_output *od = data;
|
struct osx_output *od = data;
|
||||||
size_t start, nbytes;
|
|
||||||
|
|
||||||
g_mutex_lock(od->mutex);
|
g_mutex_lock(od->mutex);
|
||||||
|
|
||||||
while (od->len >= od->buffer_size)
|
void *dest;
|
||||||
|
size_t max_length;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
dest = fifo_buffer_write(od->buffer, &max_length);
|
||||||
|
if (dest != NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
/* wait for some free space in the buffer */
|
/* wait for some free space in the buffer */
|
||||||
g_cond_wait(od->condition, od->mutex);
|
g_cond_wait(od->condition, od->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
start = od->pos + od->len;
|
if (size > max_length)
|
||||||
if (start >= od->buffer_size)
|
size = max_length;
|
||||||
start -= od->buffer_size;
|
|
||||||
|
|
||||||
nbytes = start < od->pos
|
memcpy(dest, chunk, size);
|
||||||
? od->pos - start
|
fifo_buffer_append(od->buffer, size);
|
||||||
: od->buffer_size - start;
|
|
||||||
|
|
||||||
assert(nbytes > 0);
|
|
||||||
|
|
||||||
if (nbytes > size)
|
|
||||||
nbytes = size;
|
|
||||||
|
|
||||||
memcpy(od->buffer + start, chunk, nbytes);
|
|
||||||
od->len += nbytes;
|
|
||||||
|
|
||||||
g_mutex_unlock(od->mutex);
|
g_mutex_unlock(od->mutex);
|
||||||
|
|
||||||
return nbytes;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct audio_output_plugin osxPlugin = {
|
const struct audio_output_plugin osxPlugin = {
|
||||||
|
@@ -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);
|
||||||
@@ -207,6 +208,9 @@ pulse_output_subscribe_cb(pa_context *context,
|
|||||||
static bool
|
static bool
|
||||||
pulse_output_connect(struct pulse_output *po, GError **error_r)
|
pulse_output_connect(struct pulse_output *po, GError **error_r)
|
||||||
{
|
{
|
||||||
|
assert(po != NULL);
|
||||||
|
assert(po->context != NULL);
|
||||||
|
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
error = pa_context_connect(po->context, po->server,
|
error = pa_context_connect(po->context, po->server,
|
||||||
@@ -221,14 +225,59 @@ pulse_output_connect(struct pulse_output *po, GError **error_r)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees and clears the stream.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
pulse_output_delete_stream(struct pulse_output *po)
|
||||||
|
{
|
||||||
|
assert(po != NULL);
|
||||||
|
assert(po->stream != NULL);
|
||||||
|
|
||||||
|
#if PA_CHECK_VERSION(0,9,8)
|
||||||
|
pa_stream_set_suspended_callback(po->stream, NULL, NULL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
pa_stream_set_state_callback(po->stream, NULL, NULL);
|
||||||
|
pa_stream_set_write_callback(po->stream, NULL, NULL);
|
||||||
|
|
||||||
|
pa_stream_disconnect(po->stream);
|
||||||
|
pa_stream_unref(po->stream);
|
||||||
|
po->stream = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees and clears the context.
|
||||||
|
*
|
||||||
|
* Caller must lock the main loop.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
pulse_output_delete_context(struct pulse_output *po)
|
||||||
|
{
|
||||||
|
assert(po != NULL);
|
||||||
|
assert(po->context != NULL);
|
||||||
|
|
||||||
|
pa_context_set_state_callback(po->context, NULL, NULL);
|
||||||
|
pa_context_set_subscribe_callback(po->context, NULL, NULL);
|
||||||
|
|
||||||
|
pa_context_disconnect(po->context);
|
||||||
|
pa_context_unref(po->context);
|
||||||
|
po->context = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
pulse_output_setup_context(struct pulse_output *po, GError **error_r)
|
pulse_output_setup_context(struct pulse_output *po, GError **error_r)
|
||||||
{
|
{
|
||||||
|
assert(po != NULL);
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop),
|
po->context = pa_context_new(pa_threaded_mainloop_get_api(po->mainloop),
|
||||||
MPD_PULSE_NAME);
|
MPD_PULSE_NAME);
|
||||||
if (po->context == NULL) {
|
if (po->context == NULL) {
|
||||||
@@ -243,25 +292,13 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r)
|
|||||||
pulse_output_subscribe_cb, po);
|
pulse_output_subscribe_cb, po);
|
||||||
|
|
||||||
if (!pulse_output_connect(po, error_r)) {
|
if (!pulse_output_connect(po, error_r)) {
|
||||||
pa_context_unref(po->context);
|
pulse_output_delete_context(po);
|
||||||
po->context = NULL;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees and clears the context.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
pulse_output_delete_context(struct pulse_output *po)
|
|
||||||
{
|
|
||||||
pa_context_disconnect(po->context);
|
|
||||||
pa_context_unref(po->context);
|
|
||||||
po->context = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
||||||
const struct config_param *param,
|
const struct config_param *param,
|
||||||
@@ -323,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);
|
||||||
@@ -347,6 +380,8 @@ pulse_output_disable(void *data)
|
|||||||
{
|
{
|
||||||
struct pulse_output *po = data;
|
struct pulse_output *po = data;
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
pa_threaded_mainloop_stop(po->mainloop);
|
pa_threaded_mainloop_stop(po->mainloop);
|
||||||
if (po->context != NULL)
|
if (po->context != NULL)
|
||||||
pulse_output_delete_context(po);
|
pulse_output_delete_context(po);
|
||||||
@@ -358,14 +393,16 @@ 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
|
||||||
pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
||||||
{
|
{
|
||||||
pa_context_state_t state;
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
pa_context_state_t state;
|
||||||
|
|
||||||
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;
|
||||||
@@ -375,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:
|
||||||
@@ -386,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:
|
||||||
@@ -399,11 +434,32 @@ pulse_output_wait_connection(struct pulse_output *po, GError **error_r)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PA_CHECK_VERSION(0,9,8)
|
||||||
|
|
||||||
|
static void
|
||||||
|
pulse_output_stream_suspended_cb(G_GNUC_UNUSED pa_stream *stream, void *userdata)
|
||||||
|
{
|
||||||
|
struct pulse_output *po = userdata;
|
||||||
|
|
||||||
|
assert(stream == po->stream || po->stream == NULL);
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
|
/* wake up the main loop to break out of the loop in
|
||||||
|
pulse_output_play() */
|
||||||
|
pa_threaded_mainloop_signal(po->mainloop, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pulse_output_stream_state_cb(pa_stream *stream, void *userdata)
|
pulse_output_stream_state_cb(pa_stream *stream, void *userdata)
|
||||||
{
|
{
|
||||||
struct pulse_output *po = userdata;
|
struct pulse_output *po = userdata;
|
||||||
|
|
||||||
|
assert(stream == po->stream || po->stream == NULL);
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
assert(po->context != NULL);
|
||||||
|
|
||||||
switch (pa_stream_get_state(stream)) {
|
switch (pa_stream_get_state(stream)) {
|
||||||
case PA_STREAM_READY:
|
case PA_STREAM_READY:
|
||||||
if (po->mixer != NULL)
|
if (po->mixer != NULL)
|
||||||
@@ -432,6 +488,8 @@ pulse_output_stream_write_cb(G_GNUC_UNUSED pa_stream *stream, size_t nbytes,
|
|||||||
{
|
{
|
||||||
struct pulse_output *po = userdata;
|
struct pulse_output *po = userdata;
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
po->writable = nbytes;
|
po->writable = nbytes;
|
||||||
pa_threaded_mainloop_signal(po->mainloop, 0);
|
pa_threaded_mainloop_signal(po->mainloop, 0);
|
||||||
}
|
}
|
||||||
@@ -444,6 +502,10 @@ pulse_output_open(void *data, struct audio_format *audio_format,
|
|||||||
pa_sample_spec ss;
|
pa_sample_spec ss;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
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:
|
||||||
@@ -463,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 */
|
||||||
@@ -474,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);
|
||||||
@@ -487,6 +549,11 @@ pulse_output_open(void *data, struct audio_format *audio_format,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if PA_CHECK_VERSION(0,9,8)
|
||||||
|
pa_stream_set_suspended_callback(po->stream,
|
||||||
|
pulse_output_stream_suspended_cb, po);
|
||||||
|
#endif
|
||||||
|
|
||||||
pa_stream_set_state_callback(po->stream,
|
pa_stream_set_state_callback(po->stream,
|
||||||
pulse_output_stream_state_cb, po);
|
pulse_output_stream_state_cb, po);
|
||||||
pa_stream_set_write_callback(po->stream,
|
pa_stream_set_write_callback(po->stream,
|
||||||
@@ -497,8 +564,7 @@ pulse_output_open(void *data, struct audio_format *audio_format,
|
|||||||
error = pa_stream_connect_playback(po->stream, po->sink,
|
error = pa_stream_connect_playback(po->stream, po->sink,
|
||||||
NULL, 0, NULL, NULL);
|
NULL, 0, NULL, NULL);
|
||||||
if (error < 0) {
|
if (error < 0) {
|
||||||
pa_stream_unref(po->stream);
|
pulse_output_delete_stream(po);
|
||||||
po->stream = NULL;
|
|
||||||
|
|
||||||
g_set_error(error_r, pulse_output_quark(), 0,
|
g_set_error(error_r, pulse_output_quark(), 0,
|
||||||
"pa_stream_connect_playback() has failed: %s",
|
"pa_stream_connect_playback() has failed: %s",
|
||||||
@@ -522,6 +588,8 @@ pulse_output_close(void *data)
|
|||||||
struct pulse_output *po = data;
|
struct pulse_output *po = data;
|
||||||
pa_operation *o;
|
pa_operation *o;
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
pa_threaded_mainloop_lock(po->mainloop);
|
||||||
|
|
||||||
if (pa_stream_get_state(po->stream) == PA_STREAM_READY) {
|
if (pa_stream_get_state(po->stream) == PA_STREAM_READY) {
|
||||||
@@ -534,9 +602,7 @@ pulse_output_close(void *data)
|
|||||||
pulse_wait_for_operation(po->mainloop, o);
|
pulse_wait_for_operation(po->mainloop, o);
|
||||||
}
|
}
|
||||||
|
|
||||||
pa_stream_disconnect(po->stream);
|
pulse_output_delete_stream(po);
|
||||||
pa_stream_unref(po->stream);
|
|
||||||
po->stream = NULL;
|
|
||||||
|
|
||||||
if (po->context != NULL &&
|
if (po->context != NULL &&
|
||||||
pa_context_get_state(po->context) != PA_CONTEXT_READY)
|
pa_context_get_state(po->context) != PA_CONTEXT_READY)
|
||||||
@@ -556,6 +622,8 @@ pulse_output_check_stream(struct pulse_output *po)
|
|||||||
{
|
{
|
||||||
pa_stream_state_t state = pa_stream_get_state(po->stream);
|
pa_stream_state_t state = pa_stream_get_state(po->stream);
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case PA_STREAM_READY:
|
case PA_STREAM_READY:
|
||||||
case PA_STREAM_FAILED:
|
case PA_STREAM_FAILED:
|
||||||
@@ -637,6 +705,8 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause,
|
|||||||
{
|
{
|
||||||
pa_operation *o;
|
pa_operation *o;
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
|
assert(po->context != NULL);
|
||||||
assert(po->stream != NULL);
|
assert(po->stream != NULL);
|
||||||
|
|
||||||
o = pa_stream_cork(po->stream, pause,
|
o = pa_stream_cork(po->stream, pause,
|
||||||
@@ -667,6 +737,7 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
|
|||||||
struct pulse_output *po = data;
|
struct pulse_output *po = data;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
assert(po->stream != NULL);
|
assert(po->stream != NULL);
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
pa_threaded_mainloop_lock(po->mainloop);
|
||||||
@@ -683,19 +754,30 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r)
|
|||||||
/* unpause if previously paused */
|
/* unpause if previously paused */
|
||||||
|
|
||||||
if (pulse_output_stream_is_paused(po) &&
|
if (pulse_output_stream_is_paused(po) &&
|
||||||
!pulse_output_stream_pause(po, false, error_r))
|
!pulse_output_stream_pause(po, false, error_r)) {
|
||||||
|
pa_threaded_mainloop_unlock(po->mainloop);
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* wait until the server allows us to write */
|
/* wait until the server allows us to write */
|
||||||
|
|
||||||
while (po->writable == 0) {
|
while (po->writable == 0) {
|
||||||
|
#if PA_CHECK_VERSION(0,9,8)
|
||||||
|
if (pa_stream_is_suspended(po->stream)) {
|
||||||
|
pa_threaded_mainloop_unlock(po->mainloop);
|
||||||
|
g_set_error(error_r, pulse_output_quark(), 0,
|
||||||
|
"suspended");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
pa_threaded_mainloop_wait(po->mainloop);
|
pa_threaded_mainloop_wait(po->mainloop);
|
||||||
|
|
||||||
if (pa_stream_get_state(po->stream) != PA_STREAM_READY) {
|
if (pa_stream_get_state(po->stream) != PA_STREAM_READY) {
|
||||||
pa_threaded_mainloop_unlock(po->mainloop);
|
pa_threaded_mainloop_unlock(po->mainloop);
|
||||||
g_set_error(error_r, pulse_output_quark(), 0,
|
g_set_error(error_r, pulse_output_quark(), 0,
|
||||||
"disconnected");
|
"disconnected");
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -725,6 +807,7 @@ pulse_output_cancel(void *data)
|
|||||||
struct pulse_output *po = data;
|
struct pulse_output *po = data;
|
||||||
pa_operation *o;
|
pa_operation *o;
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
assert(po->stream != NULL);
|
assert(po->stream != NULL);
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
pa_threaded_mainloop_lock(po->mainloop);
|
||||||
@@ -756,6 +839,7 @@ pulse_output_pause(void *data)
|
|||||||
struct pulse_output *po = data;
|
struct pulse_output *po = data;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
|
|
||||||
|
assert(po->mainloop != NULL);
|
||||||
assert(po->stream != NULL);
|
assert(po->stream != NULL);
|
||||||
|
|
||||||
pa_threaded_mainloop_lock(po->mainloop);
|
pa_threaded_mainloop_lock(po->mainloop);
|
||||||
|
@@ -79,23 +79,27 @@ recorder_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
|
|||||||
if (encoder_plugin == NULL) {
|
if (encoder_plugin == NULL) {
|
||||||
g_set_error(error_r, recorder_output_quark(), 0,
|
g_set_error(error_r, recorder_output_quark(), 0,
|
||||||
"No such encoder: %s", encoder_name);
|
"No such encoder: %s", encoder_name);
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
recorder->path = config_get_block_string(param, "path", NULL);
|
recorder->path = config_get_block_string(param, "path", NULL);
|
||||||
if (recorder->path == NULL) {
|
if (recorder->path == NULL) {
|
||||||
g_set_error(error_r, recorder_output_quark(), 0,
|
g_set_error(error_r, recorder_output_quark(), 0,
|
||||||
"'path' not configured");
|
"'path' not configured");
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize encoder */
|
/* initialize encoder */
|
||||||
|
|
||||||
recorder->encoder = encoder_init(encoder_plugin, param, error_r);
|
recorder->encoder = encoder_init(encoder_plugin, param, error_r);
|
||||||
if (recorder->encoder == NULL)
|
if (recorder->encoder == NULL)
|
||||||
return NULL;
|
goto failure;
|
||||||
|
|
||||||
return recorder;
|
return recorder;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
g_free(recorder);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -187,7 +191,7 @@ recorder_output_close(void *data)
|
|||||||
|
|
||||||
/* flush the encoder and write the rest to the file */
|
/* flush the encoder and write the rest to the file */
|
||||||
|
|
||||||
if (encoder_flush(recorder->encoder, NULL))
|
if (encoder_end(recorder->encoder, NULL))
|
||||||
recorder_output_encoder_to_file(recorder, NULL);
|
recorder_output_encoder_to_file(recorder, NULL);
|
||||||
|
|
||||||
/* now really close everything */
|
/* now really close everything */
|
||||||
|
@@ -152,7 +152,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
if (port == 0) {
|
if (port == 0) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"shout port must be configured");
|
"shout port must be configured");
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
check_block_param("password");
|
check_block_param("password");
|
||||||
@@ -174,21 +174,21 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
"shout quality \"%s\" is not a number in the "
|
"shout quality \"%s\" is not a number in the "
|
||||||
"range -1 to 10, line %i",
|
"range -1 to 10, line %i",
|
||||||
value, param->line);
|
value, param->line);
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
|
if (config_get_block_string(param, "bitrate", NULL) != NULL) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"quality and bitrate are "
|
"quality and bitrate are "
|
||||||
"both defined");
|
"both defined");
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value = config_get_block_string(param, "bitrate", NULL);
|
value = config_get_block_string(param, "bitrate", NULL);
|
||||||
if (value == NULL) {
|
if (value == NULL) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"neither bitrate nor quality defined");
|
"neither bitrate nor quality defined");
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
sd->bitrate = strtol(value, &test, 10);
|
sd->bitrate = strtol(value, &test, 10);
|
||||||
@@ -196,7 +196,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
if (*test != '\0' || sd->bitrate <= 0) {
|
if (*test != '\0' || sd->bitrate <= 0) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"bitrate must be a positive integer");
|
"bitrate must be a positive integer");
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,12 +206,12 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"couldn't find shout encoder plugin \"%s\"",
|
"couldn't find shout encoder plugin \"%s\"",
|
||||||
encoding);
|
encoding);
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
sd->encoder = encoder_init(encoder_plugin, param, error);
|
sd->encoder = encoder_init(encoder_plugin, param, error);
|
||||||
if (sd->encoder == NULL)
|
if (sd->encoder == NULL)
|
||||||
return NULL;
|
goto failure;
|
||||||
|
|
||||||
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
if (strcmp(encoding, "mp3") == 0 || strcmp(encoding, "lame") == 0)
|
||||||
shout_format = SHOUT_FORMAT_MP3;
|
shout_format = SHOUT_FORMAT_MP3;
|
||||||
@@ -225,7 +225,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"you cannot stream \"%s\" to shoutcast, use mp3",
|
"you cannot stream \"%s\" to shoutcast, use mp3",
|
||||||
encoding);
|
encoding);
|
||||||
return NULL;
|
goto failure;
|
||||||
} else if (0 == strcmp(value, "shoutcast"))
|
} else if (0 == strcmp(value, "shoutcast"))
|
||||||
protocol = SHOUT_PROTOCOL_ICY;
|
protocol = SHOUT_PROTOCOL_ICY;
|
||||||
else if (0 == strcmp(value, "icecast1"))
|
else if (0 == strcmp(value, "icecast1"))
|
||||||
@@ -237,7 +237,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
"shout protocol \"%s\" is not \"shoutcast\" or "
|
"shout protocol \"%s\" is not \"shoutcast\" or "
|
||||||
"\"icecast1\"or \"icecast2\"",
|
"\"icecast1\"or \"icecast2\"",
|
||||||
value);
|
value);
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
protocol = SHOUT_PROTOCOL_HTTP;
|
protocol = SHOUT_PROTOCOL_HTTP;
|
||||||
@@ -256,7 +256,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
|
shout_set_agent(sd->shout_conn, "MPD") != SHOUTERR_SUCCESS) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"%s", shout_get_error(sd->shout_conn));
|
"%s", shout_get_error(sd->shout_conn));
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* optional paramters */
|
/* optional paramters */
|
||||||
@@ -267,14 +267,14 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
|
if (value != NULL && shout_set_genre(sd->shout_conn, value)) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"%s", shout_get_error(sd->shout_conn));
|
"%s", shout_get_error(sd->shout_conn));
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
value = config_get_block_string(param, "description", NULL);
|
value = config_get_block_string(param, "description", NULL);
|
||||||
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
|
if (value != NULL && shout_set_description(sd->shout_conn, value)) {
|
||||||
g_set_error(error, shout_output_quark(), 0,
|
g_set_error(error, shout_output_quark(), 0,
|
||||||
"%s", shout_get_error(sd->shout_conn));
|
"%s", shout_get_error(sd->shout_conn));
|
||||||
return NULL;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -300,6 +300,10 @@ my_shout_init_driver(const struct audio_format *audio_format,
|
|||||||
}
|
}
|
||||||
|
|
||||||
return sd;
|
return sd;
|
||||||
|
|
||||||
|
failure:
|
||||||
|
free_shout_data(sd);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@@ -354,7 +358,7 @@ static void close_shout_conn(struct shout_data * sd)
|
|||||||
sd->buf.len = 0;
|
sd->buf.len = 0;
|
||||||
|
|
||||||
if (sd->encoder != NULL) {
|
if (sd->encoder != NULL) {
|
||||||
if (encoder_flush(sd->encoder, NULL))
|
if (encoder_end(sd->encoder, NULL))
|
||||||
write_page(sd, NULL);
|
write_page(sd, NULL);
|
||||||
|
|
||||||
encoder_close(sd->encoder);
|
encoder_close(sd->encoder);
|
||||||
@@ -494,7 +498,7 @@ shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
snprintf(dest, size, "%s - %s", title, artist);
|
snprintf(dest, size, "%s - %s", artist, title);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void my_shout_set_tag(void *data,
|
static void my_shout_set_tag(void *data,
|
||||||
@@ -507,7 +511,7 @@ static void my_shout_set_tag(void *data,
|
|||||||
if (sd->encoder->plugin->tag != NULL) {
|
if (sd->encoder->plugin->tag != NULL) {
|
||||||
/* encoder plugin supports stream tags */
|
/* encoder plugin supports stream tags */
|
||||||
|
|
||||||
ret = encoder_flush(sd->encoder, &error);
|
ret = encoder_pre_tag(sd->encoder, &error);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
g_warning("%s", error->message);
|
g_warning("%s", error->message);
|
||||||
g_error_free(error);
|
g_error_free(error);
|
||||||
|
@@ -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"
|
||||||
|
|
||||||
@@ -93,7 +111,7 @@ solaris_output_open(void *data, struct audio_format *audio_format,
|
|||||||
|
|
||||||
/* open the device in non-blocking mode */
|
/* open the device in non-blocking mode */
|
||||||
|
|
||||||
so->fd = open_cloexec(so->device, O_WRONLY|O_NONBLOCK);
|
so->fd = open_cloexec(so->device, O_WRONLY|O_NONBLOCK, 0);
|
||||||
if (so->fd < 0) {
|
if (so->fd < 0) {
|
||||||
g_set_error(error, solaris_output_quark(), errno,
|
g_set_error(error, solaris_output_quark(), errno,
|
||||||
"Failed to open %s: %s",
|
"Failed to open %s: %s",
|
||||||
|
@@ -200,11 +200,7 @@ winmm_set_buffer(struct winmm_output *wo, struct winmm_buffer *buffer,
|
|||||||
GError **error_r)
|
GError **error_r)
|
||||||
{
|
{
|
||||||
void *dest = pcm_buffer_get(&buffer->buffer, size);
|
void *dest = pcm_buffer_get(&buffer->buffer, size);
|
||||||
if (dest == NULL) {
|
assert(dest != NULL);
|
||||||
g_set_error(error_r, winmm_output_quark(), 0,
|
|
||||||
"Out of memory");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(dest, data, size);
|
memcpy(dest, data, size);
|
||||||
|
|
||||||
|
@@ -205,27 +205,14 @@ static void audio_output_wait_all(void)
|
|||||||
notify_wait(&audio_output_client_notify);
|
notify_wait(&audio_output_client_notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Signals the audio output if it is open. This function locks the
|
|
||||||
* mutex.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
audio_output_lock_signal(struct audio_output *ao)
|
|
||||||
{
|
|
||||||
g_mutex_lock(ao->mutex);
|
|
||||||
if (audio_output_is_open(ao))
|
|
||||||
g_cond_signal(ao->cond);
|
|
||||||
g_mutex_unlock(ao->mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signals all audio outputs which are open.
|
* Signals all audio outputs which are open.
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
audio_output_signal_all(void)
|
audio_output_allow_play_all(void)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < num_audio_outputs; ++i)
|
for (unsigned i = 0; i < num_audio_outputs; ++i)
|
||||||
audio_output_lock_signal(&audio_outputs[i]);
|
audio_output_allow_play(&audio_outputs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -530,7 +517,7 @@ audio_output_all_cancel(void)
|
|||||||
/* the audio outputs are now waiting for a signal, to
|
/* the audio outputs are now waiting for a signal, to
|
||||||
synchronize the cleared music pipe */
|
synchronize the cleared music pipe */
|
||||||
|
|
||||||
audio_output_signal_all();
|
audio_output_allow_play_all();
|
||||||
|
|
||||||
/* invalidate elapsed_time */
|
/* invalidate elapsed_time */
|
||||||
|
|
||||||
|
@@ -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.
|
||||||
*/
|
*/
|
||||||
@@ -115,6 +112,9 @@ audio_output_open(struct audio_output *ao,
|
|||||||
{
|
{
|
||||||
bool open;
|
bool open;
|
||||||
|
|
||||||
|
assert(ao != NULL);
|
||||||
|
assert(ao->allow_play);
|
||||||
|
assert(audio_format_valid(audio_format));
|
||||||
assert(mp != NULL);
|
assert(mp != NULL);
|
||||||
|
|
||||||
if (ao->fail_timer != NULL) {
|
if (ao->fail_timer != NULL) {
|
||||||
@@ -139,10 +139,6 @@ audio_output_open(struct audio_output *ao,
|
|||||||
/* we're not using audio_output_cancel() here,
|
/* we're not using audio_output_cancel() here,
|
||||||
because that function is asynchronous */
|
because that function is asynchronous */
|
||||||
ao_command(ao, AO_COMMAND_CANCEL);
|
ao_command(ao, AO_COMMAND_CANCEL);
|
||||||
|
|
||||||
/* the audio output is now waiting for a
|
|
||||||
signal; wake it up immediately */
|
|
||||||
g_cond_signal(ao->cond);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -180,6 +176,7 @@ static void
|
|||||||
audio_output_close_locked(struct audio_output *ao)
|
audio_output_close_locked(struct audio_output *ao)
|
||||||
{
|
{
|
||||||
assert(ao != NULL);
|
assert(ao != NULL);
|
||||||
|
assert(ao->allow_play);
|
||||||
|
|
||||||
if (ao->mixer != NULL)
|
if (ao->mixer != NULL)
|
||||||
mixer_auto_close(ao->mixer);
|
mixer_auto_close(ao->mixer);
|
||||||
@@ -222,6 +219,8 @@ audio_output_play(struct audio_output *ao)
|
|||||||
{
|
{
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
|
|
||||||
|
assert(ao->allow_play);
|
||||||
|
|
||||||
if (audio_output_is_open(ao))
|
if (audio_output_is_open(ao))
|
||||||
g_cond_signal(ao->cond);
|
g_cond_signal(ao->cond);
|
||||||
|
|
||||||
@@ -237,6 +236,7 @@ void audio_output_pause(struct audio_output *ao)
|
|||||||
mixer_auto_close(ao->mixer);
|
mixer_auto_close(ao->mixer);
|
||||||
|
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
|
assert(ao->allow_play);
|
||||||
if (audio_output_is_open(ao))
|
if (audio_output_is_open(ao))
|
||||||
ao_command_async(ao, AO_COMMAND_PAUSE);
|
ao_command_async(ao, AO_COMMAND_PAUSE);
|
||||||
g_mutex_unlock(ao->mutex);
|
g_mutex_unlock(ao->mutex);
|
||||||
@@ -246,6 +246,7 @@ void
|
|||||||
audio_output_drain_async(struct audio_output *ao)
|
audio_output_drain_async(struct audio_output *ao)
|
||||||
{
|
{
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
|
assert(ao->allow_play);
|
||||||
if (audio_output_is_open(ao))
|
if (audio_output_is_open(ao))
|
||||||
ao_command_async(ao, AO_COMMAND_DRAIN);
|
ao_command_async(ao, AO_COMMAND_DRAIN);
|
||||||
g_mutex_unlock(ao->mutex);
|
g_mutex_unlock(ao->mutex);
|
||||||
@@ -254,8 +255,24 @@ audio_output_drain_async(struct audio_output *ao)
|
|||||||
void audio_output_cancel(struct audio_output *ao)
|
void audio_output_cancel(struct audio_output *ao)
|
||||||
{
|
{
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
if (audio_output_is_open(ao))
|
|
||||||
|
if (audio_output_is_open(ao)) {
|
||||||
|
ao->allow_play = false;
|
||||||
ao_command_async(ao, AO_COMMAND_CANCEL);
|
ao_command_async(ao, AO_COMMAND_CANCEL);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock(ao->mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
audio_output_allow_play(struct audio_output *ao)
|
||||||
|
{
|
||||||
|
g_mutex_lock(ao->mutex);
|
||||||
|
|
||||||
|
ao->allow_play = true;
|
||||||
|
if (audio_output_is_open(ao))
|
||||||
|
g_cond_signal(ao->cond);
|
||||||
|
|
||||||
g_mutex_unlock(ao->mutex);
|
g_mutex_unlock(ao->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,6 +303,7 @@ void audio_output_finish(struct audio_output *ao)
|
|||||||
|
|
||||||
if (ao->thread != NULL) {
|
if (ao->thread != NULL) {
|
||||||
g_mutex_lock(ao->mutex);
|
g_mutex_lock(ao->mutex);
|
||||||
|
assert(ao->allow_play);
|
||||||
ao_command(ao, AO_COMMAND_KILL);
|
ao_command(ao, AO_COMMAND_KILL);
|
||||||
g_mutex_unlock(ao->mutex);
|
g_mutex_unlock(ao->mutex);
|
||||||
g_thread_join(ao->thread);
|
g_thread_join(ao->thread);
|
||||||
|
@@ -70,8 +70,19 @@ void audio_output_pause(struct audio_output *ao);
|
|||||||
void
|
void
|
||||||
audio_output_drain_async(struct audio_output *ao);
|
audio_output_drain_async(struct audio_output *ao);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the "allow_play" flag and send the "CANCEL" command
|
||||||
|
* asynchronously. To finish the operation, the caller has to call
|
||||||
|
* audio_output_allow_play().
|
||||||
|
*/
|
||||||
void audio_output_cancel(struct audio_output *ao);
|
void audio_output_cancel(struct audio_output *ao);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the "allow_play" and signal the thread.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
audio_output_allow_play(struct audio_output *ao);
|
||||||
|
|
||||||
void audio_output_close(struct audio_output *ao);
|
void audio_output_close(struct audio_output *ao);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -189,6 +189,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
|
|||||||
ao->really_enabled = false;
|
ao->really_enabled = false;
|
||||||
ao->open = false;
|
ao->open = false;
|
||||||
ao->pause = false;
|
ao->pause = false;
|
||||||
|
ao->allow_play = true;
|
||||||
ao->fail_timer = NULL;
|
ao->fail_timer = NULL;
|
||||||
|
|
||||||
pcm_buffer_init(&ao->cross_fade_buffer);
|
pcm_buffer_init(&ao->cross_fade_buffer);
|
||||||
|
@@ -109,6 +109,15 @@ struct audio_output {
|
|||||||
*/
|
*/
|
||||||
bool pause;
|
bool pause;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When this flag is set, the output thread will not do any
|
||||||
|
* playback. It will wait until the flag is cleared.
|
||||||
|
*
|
||||||
|
* This is used to synchronize the "clear" operation on the
|
||||||
|
* shared music pipe during the CANCEL command.
|
||||||
|
*/
|
||||||
|
bool allow_play;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If not NULL, the device has failed, and this timer is used
|
* If not NULL, the device has failed, and this timer is used
|
||||||
* to estimate how long it should stay disabled (unless
|
* to estimate how long it should stay disabled (unless
|
||||||
|
@@ -95,6 +95,8 @@ ao_filter_open(struct audio_output *ao,
|
|||||||
struct audio_format *audio_format,
|
struct audio_format *audio_format,
|
||||||
GError **error_r)
|
GError **error_r)
|
||||||
{
|
{
|
||||||
|
assert(audio_format_valid(audio_format));
|
||||||
|
|
||||||
/* the replay_gain filter cannot fail here */
|
/* the replay_gain filter cannot fail here */
|
||||||
if (ao->replay_gain_filter != NULL)
|
if (ao->replay_gain_filter != NULL)
|
||||||
filter_open(ao->replay_gain_filter, audio_format, error_r);
|
filter_open(ao->replay_gain_filter, audio_format, error_r);
|
||||||
@@ -136,6 +138,7 @@ ao_open(struct audio_output *ao)
|
|||||||
assert(!ao->open);
|
assert(!ao->open);
|
||||||
assert(ao->pipe != NULL);
|
assert(ao->pipe != NULL);
|
||||||
assert(ao->chunk == NULL);
|
assert(ao->chunk == NULL);
|
||||||
|
assert(audio_format_valid(&ao->in_audio_format));
|
||||||
|
|
||||||
if (ao->fail_timer != NULL) {
|
if (ao->fail_timer != NULL) {
|
||||||
/* this can only happen when this
|
/* this can only happen when this
|
||||||
@@ -164,6 +167,8 @@ ao_open(struct audio_output *ao)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(audio_format_valid(filter_audio_format));
|
||||||
|
|
||||||
ao->out_audio_format = *filter_audio_format;
|
ao->out_audio_format = *filter_audio_format;
|
||||||
audio_format_mask_apply(&ao->out_audio_format,
|
audio_format_mask_apply(&ao->out_audio_format,
|
||||||
&ao->config_audio_format);
|
&ao->config_audio_format);
|
||||||
@@ -303,7 +308,7 @@ ao_wait(struct audio_output *ao)
|
|||||||
GTimeVal tv;
|
GTimeVal tv;
|
||||||
g_get_current_time(&tv);
|
g_get_current_time(&tv);
|
||||||
g_time_val_add(&tv, delay * 1000);
|
g_time_val_add(&tv, delay * 1000);
|
||||||
g_cond_timed_wait(ao->cond, ao->mutex, &tv);
|
(void)g_cond_timed_wait(ao->cond, ao->mutex, &tv);
|
||||||
|
|
||||||
if (ao->command != AO_COMMAND_NONE)
|
if (ao->command != AO_COMMAND_NONE)
|
||||||
return false;
|
return false;
|
||||||
@@ -463,12 +468,9 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk)
|
|||||||
|
|
||||||
/* don't automatically reopen this device for
|
/* don't automatically reopen this device for
|
||||||
10 seconds */
|
10 seconds */
|
||||||
g_mutex_lock(ao->mutex);
|
|
||||||
|
|
||||||
assert(ao->fail_timer == NULL);
|
assert(ao->fail_timer == NULL);
|
||||||
ao->fail_timer = g_timer_new();
|
ao->fail_timer = g_timer_new();
|
||||||
|
|
||||||
g_mutex_unlock(ao->mutex);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,15 +640,14 @@ static gpointer audio_output_task(gpointer arg)
|
|||||||
|
|
||||||
case AO_COMMAND_CANCEL:
|
case AO_COMMAND_CANCEL:
|
||||||
ao->chunk = NULL;
|
ao->chunk = NULL;
|
||||||
if (ao->open)
|
|
||||||
ao_plugin_cancel(ao->plugin, ao->data);
|
|
||||||
ao_command_finished(ao);
|
|
||||||
|
|
||||||
/* the player thread will now clear our music
|
if (ao->open) {
|
||||||
pipe - wait for a notify, to give it some
|
g_mutex_unlock(ao->mutex);
|
||||||
time */
|
ao_plugin_cancel(ao->plugin, ao->data);
|
||||||
if (ao->command == AO_COMMAND_NONE)
|
g_mutex_lock(ao->mutex);
|
||||||
g_cond_wait(ao->cond, ao->mutex);
|
}
|
||||||
|
|
||||||
|
ao_command_finished(ao);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case AO_COMMAND_KILL:
|
case AO_COMMAND_KILL:
|
||||||
@@ -656,7 +657,7 @@ static gpointer audio_output_task(gpointer arg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ao->open && ao_play(ao))
|
if (ao->open && ao->allow_play && ao_play(ao))
|
||||||
/* don't wait for an event if there are more
|
/* don't wait for an event if there are more
|
||||||
chunks in the pipe */
|
chunks in the pipe */
|
||||||
continue;
|
continue;
|
||||||
|
16
src/path.c
16
src/path.c
@@ -27,6 +27,11 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#ifdef G_OS_WIN32
|
||||||
|
#include <windows.h> // for GetACP()
|
||||||
|
#include <stdio.h> // for sprintf()
|
||||||
|
#endif
|
||||||
|
|
||||||
#undef G_LOG_DOMAIN
|
#undef G_LOG_DOMAIN
|
||||||
#define G_LOG_DOMAIN "path"
|
#define G_LOG_DOMAIN "path"
|
||||||
|
|
||||||
@@ -85,11 +90,22 @@ void path_global_init(void)
|
|||||||
|
|
||||||
charset = config_get_string(CONF_FS_CHARSET, NULL);
|
charset = config_get_string(CONF_FS_CHARSET, NULL);
|
||||||
if (charset == NULL) {
|
if (charset == NULL) {
|
||||||
|
#ifndef G_OS_WIN32
|
||||||
const gchar **encodings;
|
const gchar **encodings;
|
||||||
g_get_filename_charsets(&encodings);
|
g_get_filename_charsets(&encodings);
|
||||||
|
|
||||||
if (encodings[0] != NULL && *encodings[0] != '\0')
|
if (encodings[0] != NULL && *encodings[0] != '\0')
|
||||||
charset = encodings[0];
|
charset = encodings[0];
|
||||||
|
#else /* G_OS_WIN32 */
|
||||||
|
/* Glib claims that file system encoding is always utf-8
|
||||||
|
* on native Win32 (i.e. not Cygwin).
|
||||||
|
* However this is true only if <gstdio.h> helpers are used.
|
||||||
|
* MPD uses regular <stdio.h> functions.
|
||||||
|
* Those functions use encoding determined by GetACP(). */
|
||||||
|
char win_charset[13];
|
||||||
|
sprintf(win_charset, "cp%u", GetACP());
|
||||||
|
charset = win_charset;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charset) {
|
if (charset) {
|
||||||
|
53
src/pcm_buffer.c
Normal file
53
src/pcm_buffer.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "pcm_buffer.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Align the specified size to the next 8k boundary.
|
||||||
|
*/
|
||||||
|
G_GNUC_CONST
|
||||||
|
static size_t
|
||||||
|
align_8k(size_t size)
|
||||||
|
{
|
||||||
|
return ((size - 1) | 0x1fff) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *
|
||||||
|
pcm_buffer_get(struct pcm_buffer *buffer, size_t size)
|
||||||
|
{
|
||||||
|
assert(buffer != NULL);
|
||||||
|
|
||||||
|
if (size == 0)
|
||||||
|
/* never return NULL, because NULL would be assumed to
|
||||||
|
be an error condition */
|
||||||
|
size = 1;
|
||||||
|
|
||||||
|
if (buffer->size < size) {
|
||||||
|
/* free the old buffer */
|
||||||
|
g_free(buffer->buffer);
|
||||||
|
|
||||||
|
buffer->size = align_8k(size);
|
||||||
|
buffer->buffer = g_malloc(buffer->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(buffer->size >= size);
|
||||||
|
|
||||||
|
return buffer->buffer;
|
||||||
|
}
|
@@ -22,6 +22,8 @@
|
|||||||
|
|
||||||
#include <glib.h>
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manager for a temporary buffer which grows as needed. We could
|
* Manager for a temporary buffer which grows as needed. We could
|
||||||
* allocate a new buffer every time pcm_convert() is called, but that
|
* allocate a new buffer every time pcm_convert() is called, but that
|
||||||
@@ -39,6 +41,8 @@ struct pcm_buffer {
|
|||||||
static inline void
|
static inline void
|
||||||
pcm_buffer_init(struct pcm_buffer *buffer)
|
pcm_buffer_init(struct pcm_buffer *buffer)
|
||||||
{
|
{
|
||||||
|
assert(buffer != NULL);
|
||||||
|
|
||||||
buffer->buffer = NULL;
|
buffer->buffer = NULL;
|
||||||
buffer->size = 0;
|
buffer->size = 0;
|
||||||
}
|
}
|
||||||
@@ -49,6 +53,8 @@ pcm_buffer_init(struct pcm_buffer *buffer)
|
|||||||
static inline void
|
static inline void
|
||||||
pcm_buffer_deinit(struct pcm_buffer *buffer)
|
pcm_buffer_deinit(struct pcm_buffer *buffer)
|
||||||
{
|
{
|
||||||
|
assert(buffer != NULL);
|
||||||
|
|
||||||
g_free(buffer->buffer);
|
g_free(buffer->buffer);
|
||||||
|
|
||||||
buffer->buffer = NULL;
|
buffer->buffer = NULL;
|
||||||
@@ -57,20 +63,13 @@ pcm_buffer_deinit(struct pcm_buffer *buffer)
|
|||||||
/**
|
/**
|
||||||
* Get the buffer, and guarantee a minimum size. This buffer becomes
|
* Get the buffer, and guarantee a minimum size. This buffer becomes
|
||||||
* invalid with the next pcm_buffer_get() call.
|
* invalid with the next pcm_buffer_get() call.
|
||||||
|
*
|
||||||
|
* This function will never return NULL, even if size is zero, because
|
||||||
|
* the PCM library uses the NULL return value to signal "error". An
|
||||||
|
* empty destination buffer is not always an error.
|
||||||
*/
|
*/
|
||||||
static inline void *
|
G_GNUC_MALLOC
|
||||||
pcm_buffer_get(struct pcm_buffer *buffer, size_t size)
|
void *
|
||||||
{
|
pcm_buffer_get(struct pcm_buffer *buffer, size_t size);
|
||||||
if (buffer->size < size) {
|
|
||||||
/* free the old buffer */
|
|
||||||
g_free(buffer->buffer);
|
|
||||||
|
|
||||||
/* allocate a new buffer; align at 8 kB boundaries */
|
|
||||||
buffer->size = ((size - 1) | 0x1fff) + 1;
|
|
||||||
buffer->buffer = g_malloc(buffer->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer->buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
@@ -187,5 +187,8 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk)
|
|||||||
unsigned
|
unsigned
|
||||||
music_pipe_size(const struct music_pipe *mp)
|
music_pipe_size(const struct music_pipe *mp)
|
||||||
{
|
{
|
||||||
return mp->size;
|
g_mutex_lock(mp->mutex);
|
||||||
|
unsigned size = mp->size;
|
||||||
|
g_mutex_unlock(mp->mutex);
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
@@ -20,9 +20,10 @@
|
|||||||
#ifndef MPD_PIPE_H
|
#ifndef MPD_PIPE_H
|
||||||
#define MPD_PIPE_H
|
#define MPD_PIPE_H
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#include <glib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
struct audio_format;
|
struct audio_format;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ struct music_pipe;
|
|||||||
/**
|
/**
|
||||||
* Creates a new #music_pipe object. It is empty.
|
* Creates a new #music_pipe object. It is empty.
|
||||||
*/
|
*/
|
||||||
|
G_GNUC_MALLOC
|
||||||
struct music_pipe *
|
struct music_pipe *
|
||||||
music_pipe_new(void);
|
music_pipe_new(void);
|
||||||
|
|
||||||
@@ -70,6 +72,7 @@ music_pipe_contains(const struct music_pipe *mp,
|
|||||||
* Returns the first #music_chunk from the pipe. Returns NULL if the
|
* Returns the first #music_chunk from the pipe. Returns NULL if the
|
||||||
* pipe is empty.
|
* pipe is empty.
|
||||||
*/
|
*/
|
||||||
|
G_GNUC_PURE
|
||||||
const struct music_chunk *
|
const struct music_chunk *
|
||||||
music_pipe_peek(const struct music_pipe *mp);
|
music_pipe_peek(const struct music_pipe *mp);
|
||||||
|
|
||||||
@@ -96,9 +99,11 @@ music_pipe_push(struct music_pipe *mp, struct music_chunk *chunk);
|
|||||||
/**
|
/**
|
||||||
* Returns the number of chunks currently in this pipe.
|
* Returns the number of chunks currently in this pipe.
|
||||||
*/
|
*/
|
||||||
|
G_GNUC_PURE
|
||||||
unsigned
|
unsigned
|
||||||
music_pipe_size(const struct music_pipe *mp);
|
music_pipe_size(const struct music_pipe *mp);
|
||||||
|
|
||||||
|
G_GNUC_PURE
|
||||||
static inline bool
|
static inline bool
|
||||||
music_pipe_empty(const struct music_pipe *mp)
|
music_pipe_empty(const struct music_pipe *mp)
|
||||||
{
|
{
|
||||||
|
@@ -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,16 +397,10 @@ player_check_decoder_startup(struct player *player)
|
|||||||
static bool
|
static bool
|
||||||
player_send_silence(struct player *player)
|
player_send_silence(struct player *player)
|
||||||
{
|
{
|
||||||
struct music_chunk *chunk;
|
assert(player->output_open);
|
||||||
size_t frame_size =
|
|
||||||
audio_format_frame_size(&player->play_audio_format);
|
|
||||||
/* this formula ensures that we don't send
|
|
||||||
partial frames */
|
|
||||||
unsigned num_frames = sizeof(chunk->data) / frame_size;
|
|
||||||
|
|
||||||
assert(audio_format_defined(&player->play_audio_format));
|
assert(audio_format_defined(&player->play_audio_format));
|
||||||
|
|
||||||
chunk = music_buffer_allocate(player_buffer);
|
struct music_chunk *chunk = music_buffer_allocate(player_buffer);
|
||||||
if (chunk == NULL) {
|
if (chunk == NULL) {
|
||||||
g_warning("Failed to allocate silence buffer");
|
g_warning("Failed to allocate silence buffer");
|
||||||
return false;
|
return false;
|
||||||
@@ -375,6 +410,12 @@ player_send_silence(struct player *player)
|
|||||||
chunk->audio_format = player->play_audio_format;
|
chunk->audio_format = player->play_audio_format;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
size_t frame_size =
|
||||||
|
audio_format_frame_size(&player->play_audio_format);
|
||||||
|
/* this formula ensures that we don't send
|
||||||
|
partial frames */
|
||||||
|
unsigned num_frames = sizeof(chunk->data) / frame_size;
|
||||||
|
|
||||||
chunk->times = -1.0; /* undefined time stamp */
|
chunk->times = -1.0; /* undefined time stamp */
|
||||||
chunk->length = num_frames * frame_size;
|
chunk->length = num_frames * frame_size;
|
||||||
memset(chunk->data, 0, chunk->length);
|
memset(chunk->data, 0, chunk->length);
|
||||||
@@ -396,8 +437,6 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
{
|
{
|
||||||
struct song *song = pc.next_song;
|
struct song *song = pc.next_song;
|
||||||
struct decoder_control *dc = player->dc;
|
struct decoder_control *dc = player->dc;
|
||||||
double where;
|
|
||||||
bool ret;
|
|
||||||
|
|
||||||
assert(pc.next_song != NULL);
|
assert(pc.next_song != NULL);
|
||||||
|
|
||||||
@@ -413,8 +452,7 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
|
|
||||||
/* re-start the decoder */
|
/* re-start the decoder */
|
||||||
player_dc_start(player, player->pipe);
|
player_dc_start(player, player->pipe);
|
||||||
ret = player_wait_for_decoder(player);
|
if (!player_wait_for_decoder(player)) {
|
||||||
if (!ret) {
|
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
return false;
|
return false;
|
||||||
@@ -435,8 +473,7 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
/* wait for the decoder to complete initialization */
|
/* wait for the decoder to complete initialization */
|
||||||
|
|
||||||
while (player->decoder_starting) {
|
while (player->decoder_starting) {
|
||||||
ret = player_check_decoder_startup(player);
|
if (!player_check_decoder_startup(player)) {
|
||||||
if (!ret) {
|
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
return false;
|
return false;
|
||||||
@@ -445,14 +482,13 @@ static bool player_seek_decoder(struct player *player)
|
|||||||
|
|
||||||
/* send the SEEK command */
|
/* send the SEEK command */
|
||||||
|
|
||||||
where = pc.seek_where;
|
double where = pc.seek_where;
|
||||||
if (where > pc.total_time)
|
if (where > pc.total_time)
|
||||||
where = pc.total_time - 0.1;
|
where = pc.total_time - 0.1;
|
||||||
if (where < 0.0)
|
if (where < 0.0)
|
||||||
where = 0.0;
|
where = 0.0;
|
||||||
|
|
||||||
ret = dc_seek(dc, where + song->start_ms / 1000.0);
|
if (!dc_seek(dc, where + song->start_ms / 1000.0)) {
|
||||||
if (!ret) {
|
|
||||||
/* decoder failure */
|
/* decoder failure */
|
||||||
player_command_finished();
|
player_command_finished();
|
||||||
return false;
|
return false;
|
||||||
@@ -516,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();
|
||||||
}
|
}
|
||||||
@@ -564,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();
|
||||||
@@ -583,14 +609,12 @@ static void player_process_command(struct player *player)
|
|||||||
static void
|
static void
|
||||||
update_song_tag(struct song *song, const struct tag *new_tag)
|
update_song_tag(struct song *song, const struct tag *new_tag)
|
||||||
{
|
{
|
||||||
struct tag *old_tag;
|
|
||||||
|
|
||||||
if (song_is_file(song))
|
if (song_is_file(song))
|
||||||
/* don't update tags of local files, only remote
|
/* don't update tags of local files, only remote
|
||||||
streams may change tags dynamically */
|
streams may change tags dynamically */
|
||||||
return;
|
return;
|
||||||
|
|
||||||
old_tag = song->tag;
|
struct tag *old_tag = song->tag;
|
||||||
song->tag = tag_dup(new_tag);
|
song->tag = tag_dup(new_tag);
|
||||||
|
|
||||||
if (old_tag != NULL)
|
if (old_tag != NULL)
|
||||||
@@ -626,7 +650,9 @@ play_chunk(struct song *song, struct music_chunk *chunk,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
player_lock();
|
||||||
pc.bit_rate = chunk->bit_rate;
|
pc.bit_rate = chunk->bit_rate;
|
||||||
|
player_unlock();
|
||||||
|
|
||||||
/* send the chunk to the audio outputs */
|
/* send the chunk to the audio outputs */
|
||||||
|
|
||||||
@@ -648,15 +674,14 @@ static bool
|
|||||||
play_next_chunk(struct player *player)
|
play_next_chunk(struct player *player)
|
||||||
{
|
{
|
||||||
struct decoder_control *dc = player->dc;
|
struct decoder_control *dc = player->dc;
|
||||||
struct music_chunk *chunk = NULL;
|
|
||||||
unsigned cross_fade_position;
|
|
||||||
bool success;
|
|
||||||
|
|
||||||
if (!audio_output_all_wait(64))
|
if (!audio_output_all_wait(64))
|
||||||
/* the output pipe is still large enough, don't send
|
/* the output pipe is still large enough, don't send
|
||||||
another chunk */
|
another chunk */
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
unsigned cross_fade_position;
|
||||||
|
struct music_chunk *chunk = NULL;
|
||||||
if (player->xfade == XFADE_ENABLED &&
|
if (player->xfade == XFADE_ENABLED &&
|
||||||
player_dc_at_next_song(player) &&
|
player_dc_at_next_song(player) &&
|
||||||
(cross_fade_position = music_pipe_size(player->pipe))
|
(cross_fade_position = music_pipe_size(player->pipe))
|
||||||
@@ -694,6 +719,19 @@ play_next_chunk(struct player *player)
|
|||||||
chunk->mix_ratio = nan("");
|
chunk->mix_ratio = nan("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (music_chunk_is_empty(other_chunk)) {
|
||||||
|
/* the "other" chunk was a music_chunk
|
||||||
|
which had only a tag, but no music
|
||||||
|
data - we cannot cross-fade that;
|
||||||
|
but since this happens only at the
|
||||||
|
beginning of the new song, we can
|
||||||
|
easily recover by throwing it away
|
||||||
|
now */
|
||||||
|
music_buffer_return(player_buffer,
|
||||||
|
other_chunk);
|
||||||
|
other_chunk = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
chunk->other = other_chunk;
|
chunk->other = other_chunk;
|
||||||
} else {
|
} else {
|
||||||
/* there are not enough decoded chunks yet */
|
/* there are not enough decoded chunks yet */
|
||||||
@@ -732,9 +770,7 @@ play_next_chunk(struct player *player)
|
|||||||
|
|
||||||
/* play the current chunk */
|
/* play the current chunk */
|
||||||
|
|
||||||
success = play_chunk(player->song, chunk, &player->play_audio_format);
|
if (!play_chunk(player->song, chunk, &player->play_audio_format)) {
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
music_buffer_return(player_buffer, chunk);
|
music_buffer_return(player_buffer, chunk);
|
||||||
|
|
||||||
player_lock();
|
player_lock();
|
||||||
@@ -776,11 +812,9 @@ play_next_chunk(struct player *player)
|
|||||||
static bool
|
static bool
|
||||||
player_song_border(struct player *player)
|
player_song_border(struct player *player)
|
||||||
{
|
{
|
||||||
char *uri;
|
|
||||||
|
|
||||||
player->xfade = XFADE_UNKNOWN;
|
player->xfade = XFADE_UNKNOWN;
|
||||||
|
|
||||||
uri = song_get_uri(player->song);
|
char *uri = song_get_uri(player->song);
|
||||||
g_message("played \"%s\"", uri);
|
g_message("played \"%s\"", uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
|
|
||||||
@@ -808,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,
|
||||||
@@ -831,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();
|
||||||
|
|
||||||
@@ -856,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;
|
||||||
@@ -875,19 +914,10 @@ static void do_play(struct decoder_control *dc)
|
|||||||
|
|
||||||
if (player.decoder_starting) {
|
if (player.decoder_starting) {
|
||||||
/* wait until the decoder is initialized completely */
|
/* wait until the decoder is initialized completely */
|
||||||
bool success;
|
|
||||||
const struct song *song;
|
|
||||||
|
|
||||||
success = player_check_decoder_startup(&player);
|
if (!player_check_decoder_startup(&player))
|
||||||
if (!success)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* seek to the beginning of the range */
|
|
||||||
song = decoder_current_song(dc);
|
|
||||||
if (song != NULL && song->start_ms > 0 &&
|
|
||||||
!dc_seek(dc, song->start_ms / 1000.0))
|
|
||||||
player_dc_stop(&player);
|
|
||||||
|
|
||||||
player_lock();
|
player_lock();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -970,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) */
|
||||||
@@ -1018,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);
|
||||||
|
|
||||||
@@ -1031,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();
|
||||||
@@ -1092,10 +1122,9 @@ static gpointer player_task(G_GNUC_UNUSED gpointer arg)
|
|||||||
|
|
||||||
void player_create(void)
|
void player_create(void)
|
||||||
{
|
{
|
||||||
GError *e = NULL;
|
|
||||||
|
|
||||||
assert(pc.thread == NULL);
|
assert(pc.thread == NULL);
|
||||||
|
|
||||||
|
GError *e = NULL;
|
||||||
pc.thread = g_thread_create(player_task, NULL, true, &e);
|
pc.thread = g_thread_create(player_task, NULL, true, &e);
|
||||||
if (pc.thread == NULL)
|
if (pc.thread == NULL)
|
||||||
MPD_ERROR("Failed to spawn player task: %s", e->message);
|
MPD_ERROR("Failed to spawn player task: %s", e->message);
|
||||||
|
@@ -222,10 +222,12 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
|
|||||||
playlist->error_count = 0;
|
playlist->error_count = 0;
|
||||||
|
|
||||||
if (!playlist->playing || (unsigned)playlist->current != i) {
|
if (!playlist->playing || (unsigned)playlist->current != i) {
|
||||||
/* seeking is not within the current song - first
|
/* seeking is not within the current song - prepare
|
||||||
start playing the new song */
|
song change */
|
||||||
|
|
||||||
|
playlist->playing = true;
|
||||||
|
playlist->current = i;
|
||||||
|
|
||||||
playlist_play_order(playlist, i);
|
|
||||||
queued = NULL;
|
queued = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -356,7 +356,7 @@ playlist_move_range(struct playlist *playlist,
|
|||||||
playlist->current)
|
playlist->current)
|
||||||
: -1;
|
: -1;
|
||||||
if (to < 0 && playlist->current >= 0) {
|
if (to < 0 && playlist->current >= 0) {
|
||||||
if (start <= (unsigned)currentSong && (unsigned)currentSong <= end)
|
if (start <= (unsigned)currentSong && (unsigned)currentSong < end)
|
||||||
/* no-op, can't be moved to offset of itself */
|
/* no-op, can't be moved to offset of itself */
|
||||||
return PLAYLIST_RESULT_SUCCESS;
|
return PLAYLIST_RESULT_SUCCESS;
|
||||||
to = (currentSong + abs(to)) % queue_length(&playlist->queue);
|
to = (currentSong + abs(to)) % queue_length(&playlist->queue);
|
||||||
|
@@ -72,6 +72,14 @@ apply_song_metadata(struct song *dest, const struct song *src)
|
|||||||
song_free(dest);
|
song_free(dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dest->tag != NULL && dest->tag->time > 0 &&
|
||||||
|
src->start_ms > 0 && src->end_ms == 0 &&
|
||||||
|
src->start_ms / 1000 < (unsigned)dest->tag->time)
|
||||||
|
/* the range is open-ended, and the playlist plugin
|
||||||
|
did not know the total length of the song file
|
||||||
|
(e.g. last track on a CUE file); fix it up here */
|
||||||
|
tmp->tag->time = dest->tag->time - src->start_ms / 1000;
|
||||||
|
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,11 +105,16 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (base_uri != NULL && strcmp(base_uri, ".") == 0)
|
||||||
|
/* g_path_get_dirname() returns "." when there is no
|
||||||
|
directory name in the given path; clear that now,
|
||||||
|
because it would break the database lookup
|
||||||
|
functions */
|
||||||
|
base_uri = NULL;
|
||||||
|
|
||||||
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)] != '/') {
|
||||||
@@ -112,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);
|
||||||
}
|
}
|
||||||
@@ -121,7 +135,7 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
|
|||||||
else
|
else
|
||||||
uri = g_strdup(uri);
|
uri = g_strdup(uri);
|
||||||
|
|
||||||
if (uri_has_scheme(base_uri)) {
|
if (uri_has_scheme(uri)) {
|
||||||
dest = song_remote_new(uri);
|
dest = song_remote_new(uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -51,6 +51,12 @@ playlist_vector_init(struct playlist_vector *pv)
|
|||||||
void
|
void
|
||||||
playlist_vector_deinit(struct playlist_vector *pv);
|
playlist_vector_deinit(struct playlist_vector *pv);
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
playlist_vector_is_empty(const struct playlist_vector *pv)
|
||||||
|
{
|
||||||
|
return pv->head == NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct playlist_metadata *
|
struct playlist_metadata *
|
||||||
playlist_vector_find(struct playlist_vector *pv, const char *name);
|
playlist_vector_find(struct playlist_vector *pv, const char *name);
|
||||||
|
|
||||||
|
@@ -47,7 +47,7 @@ poison_noaccess(void *p, size_t length)
|
|||||||
memset(p, 0x01, length);
|
memset(p, 0x01, length);
|
||||||
|
|
||||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||||
VALGRIND_MAKE_MEM_NOACCESS(p, length);
|
(void)VALGRIND_MAKE_MEM_NOACCESS(p, length);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -68,7 +68,7 @@ poison_undefined(void *p, size_t length)
|
|||||||
memset(p, 0x02, length);
|
memset(p, 0x02, length);
|
||||||
|
|
||||||
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
#ifdef HAVE_VALGRIND_MEMCHECK_H
|
||||||
VALGRIND_MAKE_MEM_UNDEFINED(p, length);
|
(void)VALGRIND_MAKE_MEM_UNDEFINED(p, length);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
78
src/replay_gain_ape.c
Normal file
78
src/replay_gain_ape.c
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include "replay_gain_ape.h"
|
||||||
|
#include "replay_gain_info.h"
|
||||||
|
#include "ape.h"
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
struct rg_ape_ctx {
|
||||||
|
struct replay_gain_info *info;
|
||||||
|
bool found;
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
replay_gain_ape_callback(unsigned long flags, const char *key,
|
||||||
|
const char *_value, size_t value_length, void *_ctx)
|
||||||
|
{
|
||||||
|
struct rg_ape_ctx *ctx = _ctx;
|
||||||
|
|
||||||
|
/* we only care about utf-8 text tags */
|
||||||
|
if ((flags & (0x3 << 1)) != 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
char value[16];
|
||||||
|
if (value_length >= sizeof(value))
|
||||||
|
return true;
|
||||||
|
memcpy(value, _value, value_length);
|
||||||
|
value[value_length] = 0;
|
||||||
|
|
||||||
|
if (g_ascii_strcasecmp(key, "replaygain_track_gain") == 0) {
|
||||||
|
ctx->info->tuples[REPLAY_GAIN_TRACK].gain = atof(value);
|
||||||
|
ctx->found = true;
|
||||||
|
} else if (g_ascii_strcasecmp(key, "replaygain_album_gain") == 0) {
|
||||||
|
ctx->info->tuples[REPLAY_GAIN_ALBUM].gain = atof(value);
|
||||||
|
ctx->found = true;
|
||||||
|
} else if (g_ascii_strcasecmp(key, "replaygain_track_peak") == 0) {
|
||||||
|
ctx->info->tuples[REPLAY_GAIN_TRACK].peak = atof(value);
|
||||||
|
ctx->found = true;
|
||||||
|
} else if (g_ascii_strcasecmp(key, "replaygain_album_peak") == 0) {
|
||||||
|
ctx->info->tuples[REPLAY_GAIN_ALBUM].peak = atof(value);
|
||||||
|
ctx->found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
replay_gain_ape_read(const char *path_fs, struct replay_gain_info *info)
|
||||||
|
{
|
||||||
|
struct rg_ape_ctx ctx = {
|
||||||
|
.info = info,
|
||||||
|
.found = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
return tag_ape_scan(path_fs, replay_gain_ape_callback, &ctx) &&
|
||||||
|
ctx.found;
|
||||||
|
}
|
32
src/replay_gain_ape.h
Normal file
32
src/replay_gain_ape.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2003-2010 The Music Player Daemon Project
|
||||||
|
* http://www.musicpd.org
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MPD_REPLAY_GAIN_APE_H
|
||||||
|
#define MPD_REPLAY_GAIN_APE_H
|
||||||
|
|
||||||
|
#include "check.h"
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
struct replay_gain_info;
|
||||||
|
|
||||||
|
bool
|
||||||
|
replay_gain_ape_read(const char *path_fs, struct replay_gain_info *info);
|
||||||
|
|
||||||
|
#endif
|
@@ -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>
|
||||||
@@ -33,7 +34,6 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define WINVER 0x0501
|
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#include <winsock.h>
|
#include <winsock.h>
|
||||||
#else
|
#else
|
||||||
@@ -218,7 +218,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 +252,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,6 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#else /* G_OS_WIN32 */
|
#else /* G_OS_WIN32 */
|
||||||
#define WINVER 0x0501
|
|
||||||
#include <ws2tcpip.h>
|
#include <ws2tcpip.h>
|
||||||
#include <winsock.h>
|
#include <winsock.h>
|
||||||
#endif /* G_OS_WIN32 */
|
#endif /* G_OS_WIN32 */
|
||||||
@@ -122,7 +121,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 +129,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 +137,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -116,6 +116,6 @@ int stats_print(struct client *client)
|
|||||||
(long)g_timer_elapsed(stats.timer, NULL),
|
(long)g_timer_elapsed(stats.timer, NULL),
|
||||||
(long)(pc_get_total_play_time() + 0.5),
|
(long)(pc_get_total_play_time() + 0.5),
|
||||||
stats.song_duration,
|
stats.song_duration,
|
||||||
db_get_mtime());
|
(long)db_get_mtime());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -579,8 +579,10 @@ sticker_load(const char *type, const char *uri)
|
|||||||
bool success;
|
bool success;
|
||||||
|
|
||||||
success = sticker_list_values(sticker->table, type, uri);
|
success = sticker_list_values(sticker->table, type, uri);
|
||||||
if (!success)
|
if (!success) {
|
||||||
|
sticker_free(sticker);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (g_hash_table_size(sticker->table) == 0) {
|
if (g_hash_table_size(sticker->table) == 0) {
|
||||||
/* don't return empty sticker objects */
|
/* don't return empty sticker objects */
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user