aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--INSTALL11
-rw-r--r--Makefile.am142
-rw-r--r--NEWS23
-rw-r--r--configure.ac150
-rw-r--r--doc/doxygen.conf.in (renamed from doc/doxygen.conf)4
-rw-r--r--doc/mpd.conf.517
-rw-r--r--doc/mpdconf.example6
-rw-r--r--doc/protocol.xml200
-rw-r--r--doc/user.xml314
-rw-r--r--src/ack.h2
-rw-r--r--src/aiff.c2
-rw-r--r--src/aiff.h2
-rw-r--r--src/ape.c2
-rw-r--r--src/ape.h2
-rw-r--r--src/archive/bz2_archive_plugin.c3
-rw-r--r--src/archive/bz2_archive_plugin.h2
-rw-r--r--src/archive/iso9660_archive_plugin.c3
-rw-r--r--src/archive/iso9660_archive_plugin.h2
-rw-r--r--src/archive/zzip_archive_plugin.c3
-rw-r--r--src/archive/zzip_archive_plugin.h2
-rw-r--r--src/archive_api.c2
-rw-r--r--src/archive_api.h2
-rw-r--r--src/archive_internal.h2
-rw-r--r--src/archive_list.c4
-rw-r--r--src/archive_list.h2
-rw-r--r--src/archive_plugin.c2
-rw-r--r--src/archive_plugin.h4
-rw-r--r--src/audio.c2
-rw-r--r--src/audio.h2
-rw-r--r--src/audio_check.c2
-rw-r--r--src/audio_check.h2
-rw-r--r--src/audio_format.c2
-rw-r--r--src/audio_format.h2
-rw-r--r--src/audio_parser.c2
-rw-r--r--src/audio_parser.h4
-rw-r--r--src/buffer.c2
-rw-r--r--src/buffer.h2
-rw-r--r--src/check.h2
-rw-r--r--src/chunk.c2
-rw-r--r--src/chunk.h2
-rw-r--r--src/client.c2
-rw-r--r--src/client.h19
-rw-r--r--src/client_event.c2
-rw-r--r--src/client_expire.c2
-rw-r--r--src/client_global.c2
-rw-r--r--src/client_idle.c19
-rw-r--r--src/client_idle.h45
-rw-r--r--src/client_internal.h32
-rw-r--r--src/client_list.c2
-rw-r--r--src/client_message.c96
-rw-r--r--src/client_message.h72
-rw-r--r--src/client_new.c12
-rw-r--r--src/client_process.c2
-rw-r--r--src/client_read.c2
-rw-r--r--src/client_subscribe.c123
-rw-r--r--src/client_subscribe.h59
-rw-r--r--src/client_write.c2
-rw-r--r--src/cmdline.c4
-rw-r--r--src/cmdline.h2
-rw-r--r--src/command.c570
-rw-r--r--src/command.h2
-rw-r--r--src/conf.c153
-rw-r--r--src/conf.h48
-rw-r--r--src/crossfade.c2
-rw-r--r--src/crossfade.h2
-rw-r--r--src/cue/cue_tag.c9
-rw-r--r--src/daemon.c4
-rw-r--r--src/daemon.h2
-rw-r--r--src/database.c355
-rw-r--r--src/database.h37
-rw-r--r--src/db/simple_db_plugin.c345
-rw-r--r--src/db/simple_db_plugin.h42
-rw-r--r--src/dbUtils.c324
-rw-r--r--src/dbUtils.h45
-rw-r--r--src/db_error.h45
-rw-r--r--src/db_internal.h35
-rw-r--r--src/db_plugin.h156
-rw-r--r--src/db_print.c360
-rw-r--r--src/db_print.h71
-rw-r--r--src/db_save.c176
-rw-r--r--src/db_save.h35
-rw-r--r--src/db_selection.h56
-rw-r--r--src/db_visitor.h52
-rw-r--r--src/decoder/_flac_common.c2
-rw-r--r--src/decoder/_flac_common.h2
-rw-r--r--src/decoder/_ogg_common.c2
-rw-r--r--src/decoder/_ogg_common.h2
-rw-r--r--src/decoder/audiofile_decoder_plugin.c2
-rw-r--r--src/decoder/faad_decoder_plugin.c2
-rw-r--r--src/decoder/ffmpeg_decoder_plugin.c57
-rw-r--r--src/decoder/flac_compat.h2
-rw-r--r--src/decoder/flac_decoder_plugin.c2
-rw-r--r--src/decoder/flac_metadata.c2
-rw-r--r--src/decoder/flac_metadata.h2
-rw-r--r--src/decoder/flac_pcm.c2
-rw-r--r--src/decoder/flac_pcm.h2
-rw-r--r--src/decoder/fluidsynth_decoder_plugin.c4
-rw-r--r--src/decoder/mad_decoder_plugin.c2
-rw-r--r--src/decoder/mikmod_decoder_plugin.c2
-rw-r--r--src/decoder/modplug_decoder_plugin.c2
-rw-r--r--src/decoder/mp4ff_decoder_plugin.c2
-rw-r--r--src/decoder/mpcdec_decoder_plugin.c2
-rw-r--r--src/decoder/mpg123_decoder_plugin.c44
-rw-r--r--src/decoder/oggflac_decoder_plugin.c354
-rw-r--r--src/decoder/pcm_decoder_plugin.c88
-rw-r--r--src/decoder/pcm_decoder_plugin.h33
-rw-r--r--src/decoder/sidplay_decoder_plugin.cxx2
-rw-r--r--src/decoder/sndfile_decoder_plugin.c2
-rw-r--r--src/decoder/vorbis_decoder_plugin.c2
-rw-r--r--src/decoder/wavpack_decoder_plugin.c2
-rw-r--r--src/decoder/wildmidi_decoder_plugin.c2
-rw-r--r--src/decoder_api.c32
-rw-r--r--src/decoder_api.h2
-rw-r--r--src/decoder_buffer.c2
-rw-r--r--src/decoder_buffer.h2
-rw-r--r--src/decoder_command.h2
-rw-r--r--src/decoder_control.c28
-rw-r--r--src/decoder_control.h18
-rw-r--r--src/decoder_internal.c37
-rw-r--r--src/decoder_internal.h4
-rw-r--r--src/decoder_list.c6
-rw-r--r--src/decoder_list.h2
-rw-r--r--src/decoder_plugin.c4
-rw-r--r--src/decoder_plugin.h2
-rw-r--r--src/decoder_print.c2
-rw-r--r--src/decoder_print.h2
-rw-r--r--src/decoder_thread.c31
-rw-r--r--src/decoder_thread.h2
-rw-r--r--src/despotify_utils.c121
-rw-r--r--src/despotify_utils.h67
-rw-r--r--src/directory.c55
-rw-r--r--src/directory.h14
-rw-r--r--src/directory_print.c74
-rw-r--r--src/directory_save.c8
-rw-r--r--src/directory_save.h4
-rw-r--r--src/dirvec.c2
-rw-r--r--src/dirvec.h2
-rw-r--r--src/encoder/flac_encoder.c4
-rw-r--r--src/encoder/lame_encoder.c2
-rw-r--r--src/encoder/null_encoder.c2
-rw-r--r--src/encoder/twolame_encoder.c2
-rw-r--r--src/encoder/vorbis_encoder.c2
-rw-r--r--src/encoder/wave_encoder.c2
-rw-r--r--src/encoder_api.h2
-rw-r--r--src/encoder_list.c2
-rw-r--r--src/encoder_list.h2
-rw-r--r--src/encoder_plugin.h12
-rw-r--r--src/event_pipe.c2
-rw-r--r--src/event_pipe.h2
-rw-r--r--src/exclude.c2
-rw-r--r--src/exclude.h2
-rw-r--r--src/fd_util.c25
-rw-r--r--src/fd_util.h9
-rw-r--r--src/fifo_buffer.c2
-rw-r--r--src/fifo_buffer.h2
-rw-r--r--src/filter/autoconvert_filter_plugin.c2
-rw-r--r--src/filter/autoconvert_filter_plugin.h2
-rw-r--r--src/filter/chain_filter_plugin.c2
-rw-r--r--src/filter/chain_filter_plugin.h2
-rw-r--r--src/filter/convert_filter_plugin.c2
-rw-r--r--src/filter/convert_filter_plugin.h2
-rw-r--r--src/filter/normalize_filter_plugin.c2
-rw-r--r--src/filter/null_filter_plugin.c2
-rw-r--r--src/filter/replay_gain_filter_plugin.c2
-rw-r--r--src/filter/replay_gain_filter_plugin.h2
-rw-r--r--src/filter/route_filter_plugin.c2
-rw-r--r--src/filter/volume_filter_plugin.c2
-rw-r--r--src/filter/volume_filter_plugin.h2
-rw-r--r--src/filter_config.c2
-rw-r--r--src/filter_config.h2
-rw-r--r--src/filter_internal.h2
-rw-r--r--src/filter_plugin.c2
-rw-r--r--src/filter_plugin.h10
-rw-r--r--src/filter_registry.c2
-rw-r--r--src/filter_registry.h2
-rw-r--r--src/gcc.h49
-rw-r--r--src/glib_compat.h25
-rw-r--r--src/icy_metadata.c2
-rw-r--r--src/icy_metadata.h2
-rw-r--r--src/icy_server.c4
-rw-r--r--src/icy_server.h2
-rw-r--r--src/idle.c4
-rw-r--r--src/idle.h8
-rw-r--r--src/inotify_queue.c2
-rw-r--r--src/inotify_queue.h2
-rw-r--r--src/inotify_source.c2
-rw-r--r--src/inotify_source.h2
-rw-r--r--src/inotify_update.c2
-rw-r--r--src/inotify_update.h2
-rw-r--r--src/input/archive_input_plugin.c2
-rw-r--r--src/input/archive_input_plugin.h2
-rw-r--r--src/input/cdio_paranoia_input_plugin.c389
-rw-r--r--src/input/cdio_paranoia_input_plugin.h28
-rw-r--r--src/input/curl_input_plugin.c947
-rw-r--r--src/input/curl_input_plugin.h10
-rw-r--r--src/input/despotify_input_plugin.c227
-rw-r--r--src/input/despotify_input_plugin.h25
-rw-r--r--src/input/ffmpeg_input_plugin.c50
-rw-r--r--src/input/ffmpeg_input_plugin.h2
-rw-r--r--src/input/file_input_plugin.c11
-rw-r--r--src/input/file_input_plugin.h2
-rw-r--r--src/input/mms_input_plugin.c3
-rw-r--r--src/input/mms_input_plugin.h2
-rw-r--r--src/input/rewind_input_plugin.c14
-rw-r--r--src/input/rewind_input_plugin.h2
-rw-r--r--src/input/soup_input_plugin.c381
-rw-r--r--src/input/soup_input_plugin.h25
-rw-r--r--src/input_init.c8
-rw-r--r--src/input_init.h4
-rw-r--r--src/input_internal.c51
-rw-r--r--src/input_internal.h35
-rw-r--r--src/input_plugin.h10
-rw-r--r--src/input_registry.c23
-rw-r--r--src/input_registry.h2
-rw-r--r--src/input_stream.c14
-rw-r--r--src/input_stream.h41
-rw-r--r--src/io_thread.c184
-rw-r--r--src/io_thread.h75
-rw-r--r--src/listen.c5
-rw-r--r--src/listen.h2
-rw-r--r--src/locate.c15
-rw-r--r--src/locate.h14
-rw-r--r--src/log.c44
-rw-r--r--src/log.h12
-rw-r--r--src/ls.c10
-rw-r--r--src/ls.h2
-rw-r--r--src/main.c153
-rw-r--r--src/main.h4
-rw-r--r--src/main_win32.c2
-rw-r--r--src/mapper.c2
-rw-r--r--src/mapper.h2
-rw-r--r--src/mixer/alsa_mixer_plugin.c2
-rw-r--r--src/mixer/oss_mixer_plugin.c2
-rw-r--r--src/mixer/pulse_mixer_plugin.c2
-rw-r--r--src/mixer/pulse_mixer_plugin.h2
-rw-r--r--src/mixer/raop_mixer_plugin.c67
-rw-r--r--src/mixer/roar_mixer_plugin.c133
-rw-r--r--src/mixer/software_mixer_plugin.c2
-rw-r--r--src/mixer/software_mixer_plugin.h2
-rw-r--r--src/mixer/winmm_mixer_plugin.c2
-rw-r--r--src/mixer_all.c2
-rw-r--r--src/mixer_all.h2
-rw-r--r--src/mixer_api.c2
-rw-r--r--src/mixer_api.h2
-rw-r--r--src/mixer_control.c2
-rw-r--r--src/mixer_control.h2
-rw-r--r--src/mixer_list.h4
-rw-r--r--src/mixer_plugin.h10
-rw-r--r--src/mixer_type.c2
-rw-r--r--src/mixer_type.h2
-rw-r--r--src/mpd_error.h2
-rw-r--r--src/notify.c2
-rw-r--r--src/notify.h2
-rw-r--r--src/ntp_server.c132
-rw-r--r--src/ntp_server.h44
-rw-r--r--src/open.h2
-rw-r--r--src/output/alsa_plugin.c2
-rw-r--r--src/output/ao_plugin.c2
-rw-r--r--src/output/ffado_output_plugin.c2
-rw-r--r--src/output/fifo_output_plugin.c27
-rw-r--r--src/output/httpd_client.c2
-rw-r--r--src/output/httpd_client.h2
-rw-r--r--src/output/httpd_internal.h6
-rw-r--r--src/output/httpd_output_plugin.c2
-rw-r--r--src/output/jack_output_plugin.c2
-rw-r--r--src/output/mvp_plugin.c2
-rw-r--r--src/output/null_plugin.c6
-rw-r--r--src/output/openal_plugin.c4
-rw-r--r--src/output/oss_plugin.c2
-rw-r--r--src/output/osx_plugin.c132
-rw-r--r--src/output/pipe_output_plugin.c2
-rw-r--r--src/output/pulse_output_plugin.c2
-rw-r--r--src/output/pulse_output_plugin.h2
-rw-r--r--src/output/raop_output_plugin.c950
-rw-r--r--src/output/raop_output_plugin.h129
-rw-r--r--src/output/recorder_output_plugin.c2
-rw-r--r--src/output/roar_output_plugin.h41
-rw-r--r--src/output/roar_plugin.c324
-rw-r--r--src/output/shout_plugin.c11
-rw-r--r--src/output/solaris_output_plugin.c2
-rw-r--r--src/output/winmm_output_plugin.c2
-rw-r--r--src/output/winmm_output_plugin.h2
-rw-r--r--src/output_all.c17
-rw-r--r--src/output_all.h7
-rw-r--r--src/output_api.h2
-rw-r--r--src/output_command.c6
-rw-r--r--src/output_command.h2
-rw-r--r--src/output_control.c73
-rw-r--r--src/output_control.h4
-rw-r--r--src/output_finish.c52
-rw-r--r--src/output_init.c7
-rw-r--r--src/output_internal.h11
-rw-r--r--src/output_list.c10
-rw-r--r--src/output_list.h2
-rw-r--r--src/output_plugin.h10
-rw-r--r--src/output_print.c2
-rw-r--r--src/output_print.h2
-rw-r--r--src/output_state.c2
-rw-r--r--src/output_state.h2
-rw-r--r--src/output_thread.c5
-rw-r--r--src/output_thread.h2
-rw-r--r--src/page.c2
-rw-r--r--src/page.h2
-rw-r--r--src/path.c2
-rw-r--r--src/path.h2
-rw-r--r--src/pcm_buffer.h2
-rw-r--r--src/pcm_byteswap.c2
-rw-r--r--src/pcm_byteswap.h2
-rw-r--r--src/pcm_channels.c2
-rw-r--r--src/pcm_channels.h2
-rw-r--r--src/pcm_convert.c2
-rw-r--r--src/pcm_convert.h4
-rw-r--r--src/pcm_dither.c2
-rw-r--r--src/pcm_dither.h2
-rw-r--r--src/pcm_format.c2
-rw-r--r--src/pcm_format.h2
-rw-r--r--src/pcm_mix.c2
-rw-r--r--src/pcm_mix.h2
-rw-r--r--src/pcm_pack.c2
-rw-r--r--src/pcm_pack.h2
-rw-r--r--src/pcm_prng.h2
-rw-r--r--src/pcm_resample.c2
-rw-r--r--src/pcm_resample.h2
-rw-r--r--src/pcm_resample_fallback.c2
-rw-r--r--src/pcm_resample_internal.h2
-rw-r--r--src/pcm_resample_libsamplerate.c2
-rw-r--r--src/pcm_utils.h2
-rw-r--r--src/pcm_volume.c2
-rw-r--r--src/pcm_volume.h2
-rw-r--r--src/permission.c2
-rw-r--r--src/permission.h2
-rw-r--r--src/pipe.c2
-rw-r--r--src/pipe.h2
-rw-r--r--src/player_control.c270
-rw-r--r--src/player_control.h84
-rw-r--r--src/player_thread.c327
-rw-r--r--src/player_thread.h7
-rw-r--r--src/playlist.c75
-rw-r--r--src/playlist.h95
-rw-r--r--src/playlist/asx_playlist_plugin.c2
-rw-r--r--src/playlist/asx_playlist_plugin.h2
-rw-r--r--src/playlist/cue_playlist_plugin.c2
-rw-r--r--src/playlist/cue_playlist_plugin.h2
-rw-r--r--src/playlist/despotify_playlist_plugin.c216
-rw-r--r--src/playlist/despotify_playlist_plugin.h25
-rw-r--r--src/playlist/extm3u_playlist_plugin.c5
-rw-r--r--src/playlist/extm3u_playlist_plugin.h2
-rw-r--r--src/playlist/flac_playlist_plugin.c2
-rw-r--r--src/playlist/flac_playlist_plugin.h2
-rw-r--r--src/playlist/lastfm_playlist_plugin.c4
-rw-r--r--src/playlist/lastfm_playlist_plugin.h2
-rw-r--r--src/playlist/m3u_playlist_plugin.c2
-rw-r--r--src/playlist/m3u_playlist_plugin.h2
-rw-r--r--src/playlist/pls_playlist_plugin.c2
-rw-r--r--src/playlist/pls_playlist_plugin.h2
-rw-r--r--src/playlist/rss_playlist_plugin.c2
-rw-r--r--src/playlist/rss_playlist_plugin.h2
-rw-r--r--src/playlist/xspf_playlist_plugin.c2
-rw-r--r--src/playlist/xspf_playlist_plugin.h2
-rw-r--r--src/playlist_any.c2
-rw-r--r--src/playlist_any.h2
-rw-r--r--src/playlist_control.c59
-rw-r--r--src/playlist_database.c5
-rw-r--r--src/playlist_database.h2
-rw-r--r--src/playlist_edit.c144
-rw-r--r--src/playlist_error.h49
-rw-r--r--src/playlist_global.c5
-rw-r--r--src/playlist_internal.h8
-rw-r--r--src/playlist_list.c8
-rw-r--r--src/playlist_list.h2
-rw-r--r--src/playlist_mapper.c2
-rw-r--r--src/playlist_mapper.h2
-rw-r--r--src/playlist_plugin.h2
-rw-r--r--src/playlist_print.c10
-rw-r--r--src/playlist_print.h6
-rw-r--r--src/playlist_queue.c16
-rw-r--r--src/playlist_queue.h14
-rw-r--r--src/playlist_save.c19
-rw-r--r--src/playlist_save.h13
-rw-r--r--src/playlist_song.c14
-rw-r--r--src/playlist_song.h10
-rw-r--r--src/playlist_state.c60
-rw-r--r--src/playlist_state.h11
-rw-r--r--src/playlist_vector.c2
-rw-r--r--src/playlist_vector.h2
-rw-r--r--src/poison.h2
-rw-r--r--src/queue.c276
-rw-r--r--src/queue.h35
-rw-r--r--src/queue_print.c6
-rw-r--r--src/queue_print.h2
-rw-r--r--src/queue_save.c2
-rw-r--r--src/queue_save.h2
-rw-r--r--src/refcount.h2
-rw-r--r--src/replay_gain_ape.c2
-rw-r--r--src/replay_gain_ape.h2
-rw-r--r--src/replay_gain_config.c2
-rw-r--r--src/replay_gain_config.h2
-rw-r--r--src/replay_gain_info.c2
-rw-r--r--src/replay_gain_info.h2
-rw-r--r--src/riff.c2
-rw-r--r--src/riff.h2
-rw-r--r--src/rtsp_client.c731
-rw-r--r--src/rtsp_client.h125
-rw-r--r--src/server_socket.c7
-rw-r--r--src/server_socket.h8
-rw-r--r--src/sig_handlers.c2
-rw-r--r--src/sig_handlers.h2
-rw-r--r--src/socket_util.c2
-rw-r--r--src/socket_util.h6
-rw-r--r--src/song.c2
-rw-r--r--src/song.h2
-rw-r--r--src/song_print.c17
-rw-r--r--src/song_print.h5
-rw-r--r--src/song_save.c5
-rw-r--r--src/song_save.h4
-rw-r--r--src/song_sticker.c2
-rw-r--r--src/song_sticker.h2
-rw-r--r--src/song_update.c4
-rw-r--r--src/songvec.c2
-rw-r--r--src/songvec.h2
-rw-r--r--src/state_file.c33
-rw-r--r--src/state_file.h8
-rw-r--r--src/stats.c19
-rw-r--r--src/stats.h2
-rw-r--r--src/sticker.c2
-rw-r--r--src/sticker.h4
-rw-r--r--src/sticker_print.c2
-rw-r--r--src/sticker_print.h2
-rw-r--r--src/stored_playlist.c328
-rw-r--r--src/stored_playlist.h39
-rw-r--r--src/string_util.c47
-rw-r--r--src/string_util.h77
-rw-r--r--src/strset.c2
-rw-r--r--src/strset.h2
-rw-r--r--src/tag.c2
-rw-r--r--src/tag.h4
-rw-r--r--src/tag_ape.c2
-rw-r--r--src/tag_ape.h2
-rw-r--r--src/tag_id3.c2
-rw-r--r--src/tag_id3.h2
-rw-r--r--src/tag_internal.h4
-rw-r--r--src/tag_pool.c2
-rw-r--r--src/tag_pool.h2
-rw-r--r--src/tag_print.c2
-rw-r--r--src/tag_print.h2
-rw-r--r--src/tag_rva2.c2
-rw-r--r--src/tag_rva2.h2
-rw-r--r--src/tag_save.c2
-rw-r--r--src/tag_save.h2
-rw-r--r--src/tag_table.h2
-rw-r--r--src/tcp_socket.c381
-rw-r--r--src/tcp_socket.h61
-rw-r--r--src/text_file.c2
-rw-r--r--src/text_file.h2
-rw-r--r--src/text_input_stream.c2
-rw-r--r--src/text_input_stream.h2
-rw-r--r--src/timer.c18
-rw-r--r--src/timer.h20
-rw-r--r--src/tokenizer.c9
-rw-r--r--src/tokenizer.h2
-rw-r--r--src/udp_server.c141
-rw-r--r--src/udp_server.h52
-rw-r--r--src/update.c12
-rw-r--r--src/update.h2
-rw-r--r--src/update_internal.h2
-rw-r--r--src/update_queue.c2
-rw-r--r--src/update_remove.c30
-rw-r--r--src/update_walk.c2
-rw-r--r--src/uri.c2
-rw-r--r--src/uri.h2
-rw-r--r--src/utils.c71
-rw-r--r--src/utils.h17
-rw-r--r--src/volume.c2
-rw-r--r--src/volume.h2
-rw-r--r--src/zeroconf-avahi.c2
-rw-r--r--src/zeroconf-bonjour.c2
-rw-r--r--src/zeroconf-internal.h2
-rw-r--r--src/zeroconf.c2
-rw-r--r--src/zeroconf.h2
-rw-r--r--test/dump_playlist.c12
-rw-r--r--test/read_conf.c2
-rw-r--r--test/read_mixer.c21
-rw-r--r--test/read_tags.c19
-rw-r--r--test/run_convert.c2
-rw-r--r--test/run_decoder.c12
-rw-r--r--test/run_encoder.c4
-rw-r--r--test/run_filter.c2
-rw-r--r--test/run_inotify.c2
-rw-r--r--test/run_input.c13
-rw-r--r--test/run_normalize.c2
-rw-r--r--test/run_ntp_server.c72
-rw-r--r--test/run_output.c132
-rw-r--r--test/signals.c62
-rw-r--r--test/signals.h (renamed from src/directory_print.h)12
-rw-r--r--test/software_volume.c2
-rw-r--r--test/stdbin.h2
-rw-r--r--test/test_queue_priority.c174
-rw-r--r--valgrind.suppressions645
499 files changed, 12721 insertions, 3762 deletions
diff --git a/.gitignore b/.gitignore
index 4c626dbf3..263203c08 100644
--- a/.gitignore
+++ b/.gitignore
@@ -60,3 +60,5 @@ test/dump_playlist
test/run_normalize
test/tmp
test/run_inotify
+test/test_queue_priority
+test/run_ntp_server
diff --git a/INSTALL b/INSTALL
index 54ade434d..f39b88e45 100644
--- a/INSTALL
+++ b/INSTALL
@@ -84,11 +84,6 @@ For Ogg Vorbis support. You will need libogg and libvorbis.
FLAC - http://flac.sourceforge.net/
For FLAC support. You will need version 1.1.0 or higher of libflac.
-OggFLAC - http://www.xiph.org/ogg/vorbis/ and http://flac.sourceforge.net/
-For OggFLAC support. You will need liboggflac, which can be built from the
-FLAC sources if libogg is already installed. Versions of flac 1.1.3 and
-greater will automatically detect and use OggFLAC if it's available.
-
Audio File - http://www.68k.org/~michael/audiofile/
For WAVE, AIFF, and AU support. You will need libaudiofile.
@@ -119,6 +114,9 @@ WAVE, AIFF, and many others.
libwavpack - http://www.wavpack.com/
For WavPack playback.
+despotify - https://github.com/SimonKagstrom/despotify
+For Spotify playback.
+
Optional Miscellaneous Dependencies
-----------------------------------
@@ -141,6 +139,9 @@ For the sticker database.
libcue - http://libcue.sourceforge.net/
For CUE sheet support.
+libcdio - http://www.gnu.org/software/libcdio/
+For playing audio CDs.
+
pkg-config
----------
diff --git a/Makefile.am b/Makefile.am
index 5e6747e6d..41b905be9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -78,7 +78,6 @@ mpd_headers = \
src/decoder_internal.h \
src/directory.h \
src/directory_save.h \
- src/directory_print.h \
src/database.h \
src/encoder_plugin.h \
src/encoder_list.h \
@@ -101,6 +100,7 @@ mpd_headers = \
src/decoder/flac_pcm.h \
src/decoder/_flac_common.h \
src/decoder/_ogg_common.h \
+ src/decoder/pcm_decoder_plugin.h \
src/input_init.h \
src/input_plugin.h \
src/input_registry.h \
@@ -110,6 +110,9 @@ mpd_headers = \
src/input/curl_input_plugin.h \
src/input/rewind_input_plugin.h \
src/input/mms_input_plugin.h \
+ src/input/despotify_input_plugin.h \
+ src/input/cdio_paranoia_input_plugin.h \
+ src/despotify_utils.h \
src/text_file.h \
src/text_input_stream.h \
src/icy_server.h \
@@ -142,6 +145,7 @@ mpd_headers = \
src/output/httpd_client.h \
src/output/httpd_internal.h \
src/output/pulse_output_plugin.h \
+ src/output/roar_output_plugin.h \
src/output/winmm_output_plugin.h \
src/page.h \
src/pcm_buffer.h \
@@ -161,6 +165,7 @@ mpd_headers = \
src/player_thread.h \
src/player_control.h \
src/playlist.h \
+ src/playlist_error.h \
src/playlist_internal.h \
src/playlist_print.h \
src/playlist_save.h \
@@ -180,6 +185,7 @@ mpd_headers = \
src/playlist/asx_playlist_plugin.h \
src/playlist/rss_playlist_plugin.h \
src/playlist/lastfm_playlist_plugin.h \
+ src/playlist/despotify_playlist_plugin.h \
src/playlist/cue_playlist_plugin.h \
src/playlist/flac_playlist_plugin.h \
src/poison.h \
@@ -216,6 +222,7 @@ mpd_headers = \
src/strset.h \
src/uri.h \
src/utils.h \
+ src/string_util.h \
src/volume.h \
src/zeroconf.h src/zeroconf-internal.h \
src/locate.h \
@@ -261,8 +268,14 @@ src_mpd_SOURCES = \
src/decoder_print.c \
src/directory.c \
src/directory_save.c \
- src/directory_print.c \
src/database.c \
+ src/db_error.h \
+ src/db_save.c src/db_save.h \
+ src/db_print.c src/db_print.h \
+ src/db_plugin.h \
+ src/db_visitor.h \
+ src/db_selection.h \
+ src/db/simple_db_plugin.c src/db/simple_db_plugin.h \
src/dirvec.c \
src/exclude.c \
src/fd_util.c \
@@ -278,16 +291,24 @@ src_mpd_SOURCES = \
src/client_event.c \
src/client_expire.c \
src/client_global.c \
+ src/client_idle.h \
src/client_idle.c \
src/client_list.c \
src/client_new.c \
src/client_process.c \
src/client_read.c \
src/client_write.c \
+ src/client_message.h \
+ src/client_message.c \
+ src/client_subscribe.h \
+ src/client_subscribe.c \
+ src/tcp_socket.c src/tcp_socket.h \
+ src/udp_server.c src/udp_server.h \
src/server_socket.c \
src/listen.c \
src/log.c \
src/ls.c \
+ src/io_thread.c src/io_thread.h \
src/main.c \
src/main_win32.c \
src/event_pipe.c \
@@ -349,11 +370,17 @@ src_mpd_SOURCES = \
src/strset.c \
src/uri.c \
src/utils.c \
+ src/string_util.c \
src/volume.c \
src/locate.c \
src/stored_playlist.c \
src/timer.c
+if ENABLE_DESPOTIFY
+src_mpd_SOURCES += \
+ src/despotify_utils.c
+endif
+
if ENABLE_INOTIFY
src_mpd_SOURCES += \
src/inotify_source.c \
@@ -463,6 +490,7 @@ DECODER_LIBS = \
$(CUE_LIBS)
DECODER_SRC = \
+ src/decoder/pcm_decoder_plugin.c \
src/decoder_buffer.c \
src/decoder_plugin.c \
src/decoder_list.c
@@ -510,10 +538,6 @@ if HAVE_FLAC
DECODER_SRC += src/decoder/flac_decoder_plugin.c
endif
-if HAVE_OGGFLAC
-DECODER_SRC += src/decoder/oggflac_decoder_plugin.c
-endif
-
if HAVE_AUDIOFILE
DECODER_SRC += src/decoder/audiofile_decoder_plugin.c
endif
@@ -618,11 +642,15 @@ endif
INPUT_CFLAGS = \
$(CURL_CFLAGS) \
+ $(SOUP_CFLAGS) \
+ $(CDIO_PARANOIA_CFLAGS) \
$(FFMPEG_CFLAGS) \
$(MMS_CFLAGS)
INPUT_LIBS = \
$(CURL_LIBS) \
+ $(SOUP_LIBS) \
+ $(CDIO_PARANOIA_LIBS) \
$(FFMPEG_LIBS) \
$(MMS_LIBS)
@@ -630,6 +658,7 @@ INPUT_SRC = \
src/input_init.c \
src/input_registry.c \
src/input_stream.c \
+ src/input_internal.c src/input_internal.h \
src/input/rewind_input_plugin.c \
src/input/file_input_plugin.c
@@ -638,6 +667,16 @@ INPUT_SRC += src/input/curl_input_plugin.c \
src/icy_metadata.c
endif
+if ENABLE_SOUP
+INPUT_SRC += \
+ src/input/soup_input_plugin.c \
+ src/input/soup_input_plugin.h
+endif
+
+if ENABLE_CDIO_PARANOIA
+INPUT_SRC += src/input/cdio_paranoia_input_plugin.c
+endif
+
if HAVE_FFMPEG
INPUT_SRC += src/input/ffmpeg_input_plugin.c
endif
@@ -646,6 +685,10 @@ if ENABLE_MMS
INPUT_SRC += src/input/mms_input_plugin.c
endif
+if ENABLE_DESPOTIFY
+INPUT_SRC += src/input/despotify_input_plugin.c
+endif
+
OUTPUT_CFLAGS = \
$(AO_CFLAGS) \
@@ -653,6 +696,7 @@ OUTPUT_CFLAGS = \
$(FFADO_CFLAGS) \
$(JACK_CFLAGS) \
$(OPENAL_CFLAGS) \
+ $(OPENSSL_CFLAGS) \
$(PULSE_CFLAGS) \
$(SHOUT_CFLAGS)
@@ -660,6 +704,7 @@ OUTPUT_LIBS = \
$(LIBWRAP_LDFLAGS) \
$(AO_LIBS) \
$(ALSA_LIBS) \
+ $(ROAR_LIBS) \
$(FFADO_LIBS) \
$(JACK_LIBS) \
$(OPENAL_LIBS) \
@@ -674,6 +719,7 @@ OUTPUT_API_SRC = \
src/output_state.c \
src/output_print.c \
src/output_command.c \
+ src/output_finish.c \
src/output_init.c
OUTPUT_SRC = \
@@ -693,6 +739,11 @@ OUTPUT_SRC += src/output/alsa_plugin.c
MIXER_SRC += src/mixer/alsa_mixer_plugin.c
endif
+if HAVE_ROAR
+OUTPUT_SRC += src/output/roar_plugin.c
+MIXER_SRC += src/mixer/roar_mixer_plugin.c
+endif
+
if ENABLE_FFADO_OUTPUT
OUTPUT_SRC += src/output/ffado_output_plugin.c
endif
@@ -730,6 +781,15 @@ if HAVE_OSX
OUTPUT_SRC += src/output/osx_plugin.c
endif
+if ENABLE_RAOP_OUTPUT
+OUTPUT_SRC += \
+ src/ntp_server.c src/ntp_server.h \
+ src/rtsp_client.c src/rtsp_client.h \
+ src/output/raop_output_plugin.c
+MIXER_SRC += src/mixer/raop_mixer_plugin.c
+OUTPUT_LIBS += $(OPENSSL_LIBS)
+endif
+
if HAVE_PULSE
OUTPUT_SRC += src/output/pulse_output_plugin.c
MIXER_SRC += src/mixer/pulse_mixer_plugin.c
@@ -777,6 +837,10 @@ if ENABLE_LASTFM
PLAYLIST_SRC += src/playlist/lastfm_playlist_plugin.c
endif
+if ENABLE_DESPOTIFY
+PLAYLIST_SRC += src/playlist/despotify_playlist_plugin.c
+endif
+
if HAVE_CUE
PLAYLIST_SRC += src/playlist/cue_playlist_plugin.c
endif
@@ -815,8 +879,9 @@ SPARSE_CPPFLAGS = $(DEFAULT_INCLUDES) \
-I$(shell $(CC) -print-file-name=include-fixed)
SPARSE_CPPFLAGS += -D__SCHAR_MAX__=127 -D__SHRT_MAX__=32767 \
-D__INT_MAX__=2147483647 -D__LONG_MAX__=2147483647
+SPARSE_SOURCES = $(addprefix $(top_srcdir)/,$(filter %.c,$(src_mpd_SOURCES)))
sparse-check:
- $(SPARSE) -I. $(src_mpd_CFLAGS) $(src_mpd_CPPFLAGS) $(SPARSE_FLAGS) $(SPARSE_CPPFLAGS) $(filter-out %.cxx,$(src_mpd_SOURCES))
+ $(SPARSE) -I. $(src_mpd_CFLAGS) $(src_mpd_CPPFLAGS) $(SPARSE_FLAGS) $(SPARSE_CPPFLAGS) $(SPARSE_SOURCES)
.PHONY: sparse-check
@@ -827,14 +892,19 @@ sparse-check:
if ENABLE_TEST
-TESTS =
+C_TESTS = \
+ test/test_queue_priority
+
+TESTS = $(C_TESTS)
noinst_PROGRAMS = \
+ $(C_TESTS) \
test/read_conf \
test/run_input \
test/dump_playlist \
test/run_decoder \
test/read_tags \
+ test/run_ntp_server \
test/run_filter \
test/run_output \
test/run_convert \
@@ -851,7 +921,7 @@ test_read_conf_CPPFLAGS = $(AM_CPPFLAGS) \
test_read_conf_LDADD = $(MPD_LIBS) \
$(GLIB_LIBS)
test_read_conf_SOURCES = test/read_conf.c \
- src/conf.c src/tokenizer.c src/utils.c
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c
test_run_input_CPPFLAGS = $(AM_CPPFLAGS) \
$(ARCHIVE_CFLAGS) \
@@ -862,7 +932,8 @@ test_run_input_LDADD = $(MPD_LIBS) \
$(GLIB_LIBS)
test_run_input_SOURCES = test/run_input.c \
test/stdbin.h \
- src/conf.c src/tokenizer.c src/utils.c \
+ src/io_thread.c src/io_thread.h \
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
src/tag.c src/tag_pool.c src/tag_save.c \
src/fd_util.c \
$(ARCHIVE_SRC) \
@@ -880,7 +951,8 @@ test_dump_playlist_LDADD = $(MPD_LIBS) \
$(INPUT_LIBS) \
$(GLIB_LIBS)
test_dump_playlist_SOURCES = test/dump_playlist.c \
- src/conf.c src/tokenizer.c src/utils.c \
+ src/io_thread.c src/io_thread.h \
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c\
src/uri.c \
src/song.c src/tag.c src/tag_pool.c src/tag_save.c \
src/text_input_stream.c src/fifo_buffer.c \
@@ -910,7 +982,8 @@ test_run_decoder_LDADD = $(MPD_LIBS) \
$(GLIB_LIBS)
test_run_decoder_SOURCES = test/run_decoder.c \
test/stdbin.h \
- src/conf.c src/tokenizer.c src/utils.c src/log.c \
+ src/io_thread.c src/io_thread.h \
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
src/tag.c src/tag_pool.c \
src/replay_gain_info.c \
src/uri.c \
@@ -933,7 +1006,8 @@ test_read_tags_LDADD = $(MPD_LIBS) \
$(INPUT_LIBS) $(DECODER_LIBS) \
$(GLIB_LIBS)
test_read_tags_SOURCES = test/read_tags.c \
- src/conf.c src/tokenizer.c src/utils.c src/log.c \
+ src/io_thread.c src/io_thread.h \
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
src/tag.c src/tag_pool.c \
src/replay_gain_info.c \
src/uri.c \
@@ -945,6 +1019,15 @@ test_read_tags_SOURCES = test/read_tags.c \
$(TAG_SRC) \
$(DECODER_SRC)
+test_run_ntp_server_CPPFLAGS = $(AM_CPPFLAGS)
+test_run_ntp_server_LDADD = $(MPD_LIBS) \
+ $(GLIB_LIBS)
+test_run_ntp_server_SOURCES = test/run_ntp_server.c \
+ test/signals.c test/signals.h \
+ src/io_thread.c src/io_thread.h \
+ src/udp_server.c src/udp_server.h \
+ src/ntp_server.c src/ntp_server.h
+
test_run_filter_CPPFLAGS = $(AM_CPPFLAGS)
test_run_filter_LDADD = $(MPD_LIBS) \
$(SAMPLERATE_LIBS) \
@@ -953,7 +1036,7 @@ test_run_filter_SOURCES = test/run_filter.c \
test/stdbin.h \
src/filter_plugin.c \
src/filter_registry.c \
- src/conf.c src/tokenizer.c src/utils.c \
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c \
src/pcm_volume.c src/pcm_convert.c src/pcm_byteswap.c \
src/pcm_format.c src/pcm_channels.c src/pcm_dither.c \
src/pcm_pack.c \
@@ -970,12 +1053,23 @@ if HAVE_LIBSAMPLERATE
test_run_filter_SOURCES += src/pcm_resample_libsamplerate.c
endif
+if ENABLE_DESPOTIFY
+test_read_tags_SOURCES += \
+ src/despotify_utils.c
+test_run_input_SOURCES += \
+ src/despotify_utils.c
+test_dump_playlist_SOURCES += \
+ src/despotify_utils.c
+test_run_decoder_SOURCES += \
+ src/despotify_utils.c
+endif
+
if ENABLE_ENCODER
noinst_PROGRAMS += test/run_encoder
test_run_encoder_SOURCES = test/run_encoder.c \
test/stdbin.h \
src/conf.c src/tokenizer.c \
- src/utils.c \
+ src/utils.c src/string_util.c \
src/tag.c src/tag_pool.c \
src/audio_check.c \
src/audio_format.c \
@@ -1036,7 +1130,10 @@ test_run_output_LDADD = $(MPD_LIBS) \
$(GLIB_LIBS)
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/string_util.c src/log.c \
+ src/io_thread.c src/io_thread.h \
+ src/udp_server.c src/udp_server.h \
+ src/tcp_socket.c src/tcp_socket.h \
src/audio_check.c \
src/audio_format.c \
src/audio_parser.c \
@@ -1045,7 +1142,7 @@ test_run_output_SOURCES = test/run_output.c \
src/fifo_buffer.c \
src/page.c \
src/socket_util.c \
- src/output_init.c src/output_list.c \
+ src/output_init.c src/output_finish.c src/output_list.c \
$(ENCODER_SRC) \
src/mixer_api.c \
src/mixer_control.c \
@@ -1072,7 +1169,7 @@ test_read_mixer_LDADD = $(MPD_LIBS) \
$(OUTPUT_LIBS) \
$(GLIB_LIBS)
test_read_mixer_SOURCES = test/read_mixer.c \
- src/conf.c src/tokenizer.c src/utils.c src/log.c \
+ src/conf.c src/tokenizer.c src/utils.c src/string_util.c src/log.c \
src/mixer_control.c src/mixer_api.c \
src/filter_plugin.c \
src/filter/volume_filter_plugin.c \
@@ -1100,6 +1197,12 @@ test_run_inotify_SOURCES = test/run_inotify.c \
test_run_inotify_LDADD = $(GLIB_LIBS)
endif
+test_test_queue_priority_SOURCES = \
+ src/queue.c \
+ test/test_queue_priority.c
+test_test_queue_priority_LDADD = \
+ $(GLIB_LIBS)
+
endif
@@ -1137,8 +1240,7 @@ endif
doc/api/html/index.html: doc/doxygen.conf
@mkdir -p $(@D)
- [ "$(srcdir)" = "." ] || sed '/INPUT *=/ s/\([^ ]\+\/\)/$(subst /,\/,$(srcdir))\/\1/g' $(srcdir)/doc/doxygen.conf >doc/doxygen.conf
- $(DOXYGEN) doc/doxygen.conf
+ $(DOXYGEN) $<
all-local: $(DOCBOOK_HTML) doc/api/html/index.html
diff --git a/NEWS b/NEWS
index 90d3b0fca..7f1631bfb 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,26 @@
+ver 0.17 (2011/??/??)
+* protocol:
+ - support client-to-client communication
+ - "update" and "rescan" need only "CONTROL" permission
+* input:
+ - cdio_paranoia: new input plugin to play audio CDs
+ - curl: enable CURLOPT_NETRC
+ - soup: new input plugin based on libsoup
+ - ffmpeg: support libavformat 0.7
+* decoder:
+ - mpg123: implement seeking
+ - ffmpeg: drop support for pre-0.5 ffmpeg
+ - ffmpeg: support libavformat 0.7
+ - oggflac: delete this obsolete plugin
+* output:
+ - osx: allow user to specify other audio devices
+ - raop: new output plugin
+ - shout: add possibility to set url
+ - roar: new output plugin for RoarAudio
+* state_file: add option "restore_paused"
+* cue: show CUE track numbers
+
+
ver 0.16.5 (2010/??/??)
* configure.ac: disable assertions in the non-debugging build
* pcm_format: fix 32-to-24 bit conversion (the "silence" bug)
diff --git a/configure.ac b/configure.ac
index 9a9576271..5e325265c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,11 +1,11 @@
AC_PREREQ(2.60)
-AC_INIT(mpd, 0.16.5~git, musicpd-dev-team@lists.sourceforge.net)
+AC_INIT(mpd, 0.17~git, musicpd-dev-team@lists.sourceforge.net)
AC_CONFIG_SRCDIR([src/main.c])
AM_INIT_AUTOMAKE([foreign 1.10 dist-bzip2 subdir-objects])
AM_CONFIG_HEADER(config.h)
AC_CONFIG_MACRO_DIR([m4])
-AC_DEFINE(PROTOCOL_VERSION, "0.16.0", [The MPD protocol version])
+AC_DEFINE(PROTOCOL_VERSION, "0.17.0", [The MPD protocol version])
dnl ---------------------------------------------------------------------------
@@ -123,6 +123,11 @@ AC_ARG_ENABLE(alsa,
AS_HELP_STRING([--enable-alsa], [enable ALSA support]),,
[enable_alsa=auto])
+AC_ARG_ENABLE(roar,
+ AS_HELP_STRING([--enable-roar],
+ [enable support for RoarAudio]),,
+ [enable_roar=auto])
+
AC_ARG_ENABLE(ao,
AS_HELP_STRING([--enable-ao],
[enable support for libao]),,
@@ -138,6 +143,11 @@ AC_ARG_ENABLE(bzip2,
[enable bzip2 archive support (default: disabled)]),,
enable_bzip2=no)
+AC_ARG_ENABLE(cdio-paranoia,
+ AS_HELP_STRING([--enable-cdio-paranoia],
+ [enable support for audio CD support]),,
+ enable_cdio_paranoia=auto)
+
AC_ARG_ENABLE(cue,
AS_HELP_STRING([--enable-cue],
[enable support for libcue support]),,
@@ -148,6 +158,11 @@ AC_ARG_ENABLE(curl,
[enable support for libcurl HTTP streaming (default: auto)]),,
[enable_curl=auto])
+AC_ARG_ENABLE(soup,
+ AS_HELP_STRING([--enable-soup],
+ [enable support for libsoup HTTP streaming (default: auto)]),,
+ [enable_soup=auto])
+
AC_ARG_ENABLE(debug,
AS_HELP_STRING([--enable-debug],
[enable debugging (default: disabled)]),,
@@ -197,6 +212,11 @@ AC_ARG_ENABLE(httpd-output,
[enables the HTTP server output]),,
[enable_httpd_output=auto])
+AC_ARG_ENABLE(raop-output,
+ AS_HELP_STRING([--enable-raop-output],
+ [enables the RAOP output]),,
+ [enable_raop_output=auto])
+
AC_ARG_ENABLE(id3,
AS_HELP_STRING([--enable-id3],
[disable id3 support]),,
@@ -229,6 +249,11 @@ AC_ARG_ENABLE(lastfm,
[enable support for last.fm radio (default: disable)]),,
[enable_lastfm=no])
+AC_ARG_ENABLE(despotify,
+ AS_HELP_STRING([--enable-despotify],
+ [enable support for despotify (default: disable)]),,
+ [enable_despotify=no])
+
AC_ARG_ENABLE(lame-encoder,
AS_HELP_STRING([--enable-lame-encoder],
[enable the LAME mp3 encoder]),,
@@ -278,11 +303,6 @@ AC_ARG_ENABLE(mvp,
[enable support for Hauppauge Media MVP (default: disable)]),,
enable_mvp=no)
-AC_ARG_ENABLE(oggflac,
- AS_HELP_STRING([--disable-oggflac],
- [disable OggFLAC support (default: enable)]),,
- enable_oggflac=yes)
-
AC_ARG_ENABLE(openal,
AS_HELP_STRING([--enable-openal],
[enable OpenAL support (default: disable)]),,
@@ -623,6 +643,14 @@ if test x$enable_curl = xyes; then
fi
AM_CONDITIONAL(ENABLE_CURL, test x$enable_curl = xyes)
+dnl ----------------------------------- SOUP ----------------------------------
+MPD_AUTO_PKG(soup, SOUP, [libsoup-2.4],
+ [libsoup HTTP streaming], [libsoup not found])
+if test x$enable_soup = xyes; then
+ AC_DEFINE(ENABLE_SOUP, 1, [Define when libsoup is used for HTTP streaming])
+fi
+AM_CONDITIONAL(ENABLE_SOUP, test x$enable_soup = xyes)
+
dnl --------------------------------- Last.FM ---------------------------------
if test x$enable_lastfm = xyes; then
if test x$enable_curl != xyes; then
@@ -633,6 +661,25 @@ if test x$enable_lastfm = xyes; then
fi
AM_CONDITIONAL(ENABLE_LASTFM, test x$enable_lastfm = xyes)
+dnl --------------------------------- Despotify ---------------------------------
+MPD_AUTO_PKG(despotify, DESPOTIFY, [despotify],
+ [Despotify support], [despotify not found])
+if test x$enable_despotify = xyes; then
+ AC_DEFINE(ENABLE_DESPOTIFY, 1, [Define when despotify is enabled])
+ MPD_LIBS="$MPD_LIBS $DESPOTIFY_LIBS"
+fi
+AM_CONDITIONAL(ENABLE_DESPOTIFY, test x$enable_despotify = xyes)
+
+dnl ---------------------------------- libcue ---------------------------------
+MPD_AUTO_PKG(cdio_paranoia, CDIO_PARANOIA, [libcdio_paranoia],
+ [libcdio_paranoia audio CD library], [libcdio_paranoia not found])
+if test x$enable_cdio_paranoia = xyes; then
+ AC_DEFINE([ENABLE_CDIO_PARANOIA], 1,
+ [Define to enable libcdio_paranoia support])
+fi
+
+AM_CONDITIONAL(ENABLE_CDIO_PARANOIA, test x$enable_cdio_paranoia = xyes)
+
dnl ---------------------------------- libmms ---------------------------------
MPD_AUTO_PKG(mms, MMS, [libmms >= 0.4],
[libmms mms:// protocol support], [libmms not found])
@@ -726,21 +773,10 @@ AM_CONDITIONAL(HAVE_FAAD, test x$enable_aac = xyes)
AM_CONDITIONAL(HAVE_MP4, test x$enable_mp4 = xyes)
dnl ---------------------------------- ffmpeg ---------------------------------
-MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52 libavcodec >= 51 libavutil >= 49],
+MPD_AUTO_PKG(ffmpeg, FFMPEG, [libavformat >= 52.31 libavcodec >= 52.20 libavutil >= 49.15],
[ffmpeg decoder library], [libavformat+libavcodec+libavutil not found])
if test x$enable_ffmpeg = xyes; then
- # prior to ffmpeg svn12865, you had to specify include files
- # without path prefix
- old_CPPCFLAGS=$CPPFLAGS
- CPPFLAGS="$CPPFLAGS $FFMPEG_CFLAGS"
- AC_CHECK_HEADER(libavcodec/avcodec.h,,
- AC_DEFINE(OLD_FFMPEG_INCLUDES, 1,
- [Define if avcodec.h instead of libavcodec/avcodec.h should be included]))
- CPPCFLAGS=$old_CPPFLAGS
-fi
-
-if test x$enable_ffmpeg = xyes; then
AC_DEFINE(HAVE_FFMPEG, 1, [Define for FFMPEG support])
fi
@@ -753,25 +789,6 @@ MPD_AUTO_PKG(flac, FLAC, [flac >= 1.1],
if test x$enable_flac = xyes; then
AC_DEFINE(HAVE_FLAC, 1, [Define for FLAC support])
-
- oldcflags="$CFLAGS"
- oldlibs="$LIBS"
- CFLAGS="$CFLAGS $FLAC_CFLAGS"
- LIBS="$LIBS $FLAC_LIBS"
- if test x$enable_flac = xyes && test x$enable_oggflac = xyes; then
- AC_CHECK_DECL(FLAC_API_SUPPORTS_OGG_FLAC,
- [enable_oggflac=flac], [],
- [#include <FLAC/export.h>])
- fi
- CFLAGS="$oldcflags"
- LIBS="$oldlibs"
-
- if test x$enable_oggflac = xflac; then
- PKG_CHECK_MODULES(OGG, [ogg],
- [FLAC_LIBS="${FLAC_LIBS} ${OGG_LIBS}" FLAC_CFLAGS="${FLAC_CFLAGS} ${OGG_CFLAGS}"],
- [enable_oggflac=yes;
- AC_MSG_WARN("FLAC has the ogg API built in, but couldn't find ogg. Disabling oggflac.")])
- fi
fi
AM_CONDITIONAL(HAVE_FLAC, test x$enable_flac = xyes)
@@ -947,25 +964,6 @@ fi
AC_SUBST(TREMOR_CFLAGS)
AC_SUBST(TREMOR_LIBS)
-dnl --------------------------------- OggFLAC ---------------------------------
-dnl OggFLAC must go after Ogg Tremor
-
-if test x$enable_tremor = xyes && test x$enable_oggflac = xyes; then
- AC_MSG_WARN([disabling OggFLAC support because it is incompatible with tremor])
- enable_oggflac=no
-fi
-
-if test x$enable_oggflac = xyes; then
- AC_CHECK_HEADER([OggFLAC/stream_decoder.h],, enable_oggflac=no)
-fi
-
-if test x$enable_oggflac = xyes; then
- AC_DEFINE(HAVE_OGGFLAC,1,[Define for OggFLAC support])
- MPD_LIBS="$MPD_LIBS -lOggFLAC -lFLAC -lm"
-fi
-
-AM_CONDITIONAL(HAVE_OGGFLAC, test x$enable_oggflac = xyes)
-
dnl -------------------------------- Ogg Vorbis -------------------------------
if test x$enable_tremor = xyes; then
@@ -1065,7 +1063,6 @@ if
test x$enable_mp4 = xno &&
test x$enable_mpc = xno &&
test x$enable_mpg123 = xno &&
- test x$enable_oggflac = xno &&
test x$enable_sidplay = xno &&
test x$enable_tremor = xno &&
test x$enable_vorbis = xno &&
@@ -1076,10 +1073,10 @@ if
fi
AM_CONDITIONAL(HAVE_OGG_COMMON,
- test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_oggflac = xyes || test x$enable_flac = xyes)
+ test x$enable_vorbis = xyes || test x$enable_tremor = xyes || test x$enable_flac = xyes)
AM_CONDITIONAL(HAVE_FLAC_COMMON,
- test x$enable_flac = xyes || test x$enable_oggflac = xyes)
+ test x$enable_flac = xyes)
dnl ---------------------------------------------------------------------------
dnl Encoders for Streaming Audio Output Plugins
@@ -1202,6 +1199,16 @@ fi
AM_CONDITIONAL(HAVE_ALSA, test x$enable_alsa = xyes)
+dnl ----------------------------------- ROAR ----------------------------------
+MPD_AUTO_PKG(roar, ROAR, [libroar >= 0.4.0],
+ [ROAR output plugin], [libroar not found])
+
+if test x$enable_roar = xyes; then
+ AC_DEFINE(HAVE_ROAR, 1, [Define to enable ROAR support])
+fi
+
+AM_CONDITIONAL(HAVE_ROAR, test x$enable_roar = xyes)
+
dnl ----------------------------------- FFADO ---------------------------------
MPD_AUTO_PKG(ffado, FFADO, [libffado],
@@ -1312,7 +1319,7 @@ enable_osx=no
case "$host_os" in
darwin*)
AC_DEFINE(HAVE_OSX, 1, [Define for compiling OS X support])
- MPD_LIBS="$MPD_LIBS -framework AudioUnit -framework CoreServices"
+ MPD_LIBS="$MPD_LIBS -framework AudioUnit -framework CoreAudio -framework CoreServices"
enable_osx=yes ;;
esac
@@ -1383,6 +1390,17 @@ esac
AM_CONDITIONAL(ENABLE_SOLARIS_OUTPUT, test x$enable_solaris_output = xyes)
+dnl --------------------------------- RAOP ------------------------------------
+
+MPD_AUTO_PKG(raop_output, OPENSSL, [openssl],
+ [RAOP output], [OpenSSL not found])
+
+if test x$enable_raop_output = xyes; then
+ AC_DEFINE(ENABLE_RAOP_OUTPUT, 1, [Define for compiling RAOP support])
+fi
+
+AM_CONDITIONAL(ENABLE_RAOP_OUTPUT, test x$enable_raop_output = xyes)
+
dnl --------------------------------- WinMM ---------------------------------
case "$host_os" in
@@ -1402,6 +1420,7 @@ AM_CONDITIONAL(ENABLE_WINMM_OUTPUT, test x$enable_winmm_output = xyes)
dnl --------------------- Post Audio Output Plugins Tests ---------------------
if
test x$enable_alsa = xno &&
+ test x$enable_roar = xno &&
test x$enable_ao = xno &&
test x$enable_ffado = xno &&
test x$enable_fifo = xno &&
@@ -1411,6 +1430,7 @@ if
test x$enable_openal = xno &&
test x$enable_oss = xno &&
test x$enable_osx = xno &&
+ test x$enable_raop_output = xno &&
test x$enable_pipe_output = xno &&
test x$enable_pulse = xno &&
test x$enable_recorder_output = xno &&
@@ -1460,7 +1480,6 @@ if test x$GCC = xyes
then
MPD_CHECK_FLAG([-Wall])
MPD_CHECK_FLAG([-Wextra])
- MPD_CHECK_FLAG([-Wno-deprecated-declarations])
MPD_CHECK_FLAG([-Wmissing-prototypes])
MPD_CHECK_FLAG([-Wshadow])
MPD_CHECK_FLAG([-Wpointer-arith])
@@ -1519,7 +1538,6 @@ results(mad, [MAD])
results(mpg123, [MPG123])
results(mp4, [MP4])
results(mpc, [Musepack])
-results(oggflac, [OggFLAC], flac)
printf '\n\t'
results(tremor, [OggTremor])
results(vorbis, [OggVorbis])
@@ -1538,10 +1556,12 @@ results(id3,[ID3])
printf '\nPlayback support:\n\t'
results(alsa,ALSA)
+results(roar,ROAR)
results(ffado,FFADO)
results(fifo,FIFO)
results(recorder_output,[File Recorder])
results(httpd_output,[HTTP Daemon])
+results(raop_output, [RAOP])
results(jack,[JACK])
results(ao,[libao])
results(oss,[OSS])
@@ -1570,8 +1590,11 @@ fi
printf '\nStreaming support:\n\t'
results(curl,[CURL])
+results(soup, [SOUP])
results(lastfm,[Last.FM])
results(mms,[MMS])
+results(cdio_paranoia, [CDIO_PARANOIA])
+results(despotify,[Despotify])
printf '\n\n##########################################\n\n'
@@ -1581,5 +1604,6 @@ dnl ---------------------------------------------------------------------------
dnl Generate files
dnl ---------------------------------------------------------------------------
AC_OUTPUT(Makefile)
+AC_OUTPUT(doc/doxygen.conf)
echo 'MPD is ready for compilation, type "make" to begin.'
diff --git a/doc/doxygen.conf b/doc/doxygen.conf.in
index ddece77e8..0657c5ca2 100644
--- a/doc/doxygen.conf
+++ b/doc/doxygen.conf.in
@@ -31,7 +31,7 @@ PROJECT_NAME = MPD
# This could be handy for archiving the generated documentation or
# if some version control system is used.
-PROJECT_NUMBER =
+PROJECT_NUMBER = @VERSION@
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
@@ -534,7 +534,7 @@ WARN_LOGFILE =
# directories like "/usr/src/myproject". Separate the files or directories
# with spaces.
-INPUT = src/
+INPUT = @abs_top_srcdir@/src/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
diff --git a/doc/mpd.conf.5 b/doc/mpd.conf.5
index 4cddd7ba9..af65e1146 100644
--- a/doc/mpd.conf.5
+++ b/doc/mpd.conf.5
@@ -69,6 +69,9 @@ mpd will be saved to this file when mpd is terminated by a TERM signal or by
the "kill" command. When mpd is restarted, it will read the state file and
restore the state of mpd (including the playlist).
.TP
+.B restore_paused <yes or no>
+Put MPD into pause mode instead of starting playback after startup.
+.TP
.B user <username>
This specifies the user that MPD will run as, if set. MPD should
never run as root, and you may use this option to make MPD change its
@@ -259,6 +262,17 @@ of database.
.B auto_update_depth <N>
Limit the depth of the directories being watched, 0 means only watch
the music directory itself. There is no limit by default.
+.TP
+.B despotify_user <name>
+This specifies the user to use when logging in to Spotify using the despotify plugins.
+.TP
+.B despotify_password <name>
+This specifies the password to use when logging in to Spotify using the despotify plugins.
+.TP
+.B despotify_high_bitrate <yes or no>
+This specifies if the requested bitrate for Spotify should be high or not. Higher sounds
+better but requires more processing and higher bandwidth. Default is yes.
+.TP
.SH REQUIRED AUDIO OUTPUT PARAMETERS
.TP
.B type <type>
@@ -464,6 +478,9 @@ connect to the icecast server. The default is 2 seconds.
.B description <description>
This specifies a description of the stream.
.TP
+.B url <url>
+This specifies a URL associated with the stream.
+.TP
.B genre <genre>
This specifies the genre(s) of the stream.
.SH FILES
diff --git a/doc/mpdconf.example b/doc/mpdconf.example
index b14337c76..1aa9cf1dc 100644
--- a/doc/mpdconf.example
+++ b/doc/mpdconf.example
@@ -103,6 +103,11 @@
#
#gapless_mp3_playback "yes"
#
+# Setting "restore_paused" to "yes" puts MPD into pause mode instead
+# of starting playback after startup.
+#
+#restore_paused "no"
+#
# This setting enables MPD to create playlists in a format usable by other
# music players.
#
@@ -235,6 +240,7 @@ input {
## protocol "icecast2" # optional
## user "source" # optional
## description "My Stream Description" # optional
+## url "http://example.com" # optional
## genre "jazz" # optional
## public "no" # optional
## timeout "2" # optional
diff --git a/doc/protocol.xml b/doc/protocol.xml
index 0b4f0d175..aba080a6a 100644
--- a/doc/protocol.xml
+++ b/doc/protocol.xml
@@ -204,6 +204,47 @@
</chapter>
<chapter>
+ <title>Recipes</title>
+
+ <section>
+ <title>Queuing</title>
+
+ <para>
+ Often, users run MPD with "<link
+ linkend="command_random">random</link>" enabled, but want to
+ be able to insert songs "before" the rest of the playlist.
+ That is commonly called "queuing".
+ </para>
+
+ <para>
+ MPD implements this by allowing the client to specify a
+ "priority" for each song in the playlist (commands <link
+ linkend="command_prio"><command>prio</command></link> and
+ <link
+ linkend="command_prioid"><command>prioid</command></link>). A
+ higher priority means that the song is going to be played
+ before the other songs.
+ </para>
+
+ <para>
+ In "random" mode, MPD maintains an internal randomized
+ sequence of songs. In this sequence, songs with a higher
+ priority come first, and all songs with the same priority are
+ shuffled (by default, all songs are shuffled, because all have
+ the same priority "0"). When you increase the priority of a
+ song, it is moved to the front of the sequence according to
+ its new priority, but always after the current one. A song
+ that has been played already (it's "before" the current song
+ in that sequence) will only be scheduled for repeated playback
+ if its priority has become bigger than the priority of the
+ current song. Decreasing the priority of a song will moved it
+ farther to the end of the sequence. Changing the priority of
+ the current song has no effect on the sequence.
+ </para>
+ </section>
+ </chapter>
+
+ <chapter>
<title>Command reference</title>
<note>
@@ -318,6 +359,25 @@
<option>crossfade</option>, replay gain
</para>
</listitem>
+ <listitem>
+ <para>
+ <returnvalue>sticker</returnvalue>: the sticker database
+ has been modified.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <returnvalue>subscription</returnvalue>: a client
+ has subscribed or unsubscribed to a channel
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ <returnvalue>message</returnvalue>: a message was
+ received on a channel this client is subscribed to;
+ this event is only emitted when the queue is empty
+ </para>
+ </listitem>
</itemizedlist>
<para>
While a client is waiting for <command>idle</command>
@@ -1073,6 +1133,46 @@ OK
</para>
</listitem>
</varlistentry>
+
+ <varlistentry id="command_prio">
+ <term>
+ <cmdsynopsis>
+ <command>prio</command>
+ <arg choice="req"><replaceable>PRIORITY</replaceable></arg>
+ <arg choice="req" rep="repeat"><replaceable>START:END</replaceable></arg>
+ </cmdsynopsis>
+ </term>
+ <listitem>
+ <para>
+ Set the priority of the specified songs. A higher
+ priority means that it will be played first when
+ "random" mode is enabled.
+ </para>
+
+ <para>
+ A priority is an integer between 0 and 255. The default
+ priority of new songs is 0.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="command_prioid">
+ <term>
+ <cmdsynopsis>
+ <command>prioid</command>
+ <arg choice="req"><replaceable>PRIORITY</replaceable></arg>
+ <arg choice="req" rep="repeat"><replaceable>ID</replaceable></arg>
+ </cmdsynopsis>
+ </term>
+ <listitem>
+ <para>
+ Same as <link
+ linkend="command_prio"><command>prio</command></link>,
+ but address the songs with their id.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="command_shuffle">
<term>
<cmdsynopsis>
@@ -1790,5 +1890,105 @@ suffix: mpc</programlisting>
</varlistentry>
</variablelist>
</section>
+
+ <section>
+ <title>Client to client</title>
+
+ <para>
+ Clients can communicate with each others over "channels". A
+ channel is created by a client subscribing to it. More than
+ one client can be subscribed to a channel at a time; all of
+ them will receive the messages which get sent to it.
+ </para>
+
+ <para>
+ Each time a client subscribes or unsubscribes, the global idle
+ event <varname>subscription</varname> is generated. In
+ conjunction with the <command>channels</command> command, this
+ may be used to auto-detect clients providing additional
+ services.
+ </para>
+
+ <para>
+ A new messages is indicated by the <varname>message</varname>
+ idle event.
+ </para>
+
+ <variablelist>
+ <varlistentry id="command_subscribe">
+ <term>
+ <cmdsynopsis>
+ <command>subscribe</command>
+ <arg choice="req"><replaceable>NAME</replaceable></arg>
+ </cmdsynopsis>
+ </term>
+ <listitem>
+ <para>
+ Subscribe to a channel. The channel is created if it
+ does not exist already. The name may consist of
+ alphanumeric ASCII characters plus underscore, dash, dot
+ and colon.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="command_unsubscribe">
+ <term>
+ <cmdsynopsis>
+ <command>unsubscribe</command>
+ <arg choice="req"><replaceable>NAME</replaceable></arg>
+ </cmdsynopsis>
+ </term>
+ <listitem>
+ <para>
+ Unsubscribe from a channel.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="command_channels">
+ <term>
+ <cmdsynopsis>
+ <command>channels</command>
+ </cmdsynopsis>
+ </term>
+ <listitem>
+ <para>
+ Obtain a list of all channels. The response is a list
+ of "channel:" lines.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="command_readmessages">
+ <term>
+ <cmdsynopsis>
+ <command>readmessages</command>
+ </cmdsynopsis>
+ </term>
+ <listitem>
+ <para>
+ Reads messages for this client. The response is a list
+ of "channel:" and "message:" lines.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="command_sendmessage">
+ <term>
+ <cmdsynopsis>
+ <command>sendmessage</command>
+ <arg choice="req"><replaceable>CHANNEL</replaceable></arg>
+ <arg choice="req"><replaceable>TEXT</replaceable></arg>
+ </cmdsynopsis>
+ </term>
+ <listitem>
+ <para>
+ Send a message to the specified channel.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </section>
</chapter>
</book>
diff --git a/doc/user.xml b/doc/user.xml
index 6a9871007..4ca91da33 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -236,6 +236,16 @@ cd mpd-version</programlisting>
</section>
<section>
+ <title>Configuring encoder plugins</title>
+
+ <para>
+ Encoders are used by some of the output plugins (such as
+ <varname>shout</varname>). The encoder settings are included
+ in the <varname>audio_output</varname> section.
+ </para>
+ </section>
+
+ <section>
<title>Configuring audio outputs</title>
<para>
@@ -346,7 +356,7 @@ cd mpd-version</programlisting>
If set to "yes", then MPD attempts to keep this audio
output always open. This may be useful for streaming
servers, when you don't want to disconnect all
- listeners even when playback is accidently stopped.
+ listeners even when playback is accidentally stopped.
</entry>
</row>
<row>
@@ -621,6 +631,106 @@ cd mpd-version</programlisting>
Plays streams with the MMS protocol.
</para>
</section>
+
+ <section>
+ <title><varname>cdio_paranoia</varname></title>
+
+ <para>
+ Plays audio CDs. The URI has the form:
+ "<filename>cdda://[DEVICE][/TRACK]</filename>". The
+ simplest form <filename>cdda://</filename> plays the whole
+ disc in the default drive.
+ </para>
+ </section>
+
+ <section>
+ <title><varname>despotify</varname></title>
+
+ <para>
+ Plays <ulink url="http://www.spotify.com">Spotify</ulink> tracks using the despotify
+ library. The despotify plugin uses a <filename>spt://</filename> URI and a Spotify
+ URL. So for example, you can add a song with:
+ </para>
+
+ <para>
+ <filename>mpc add spt://spotify:track:5qENVY0YEdZ7fiuOax70x1</filename>
+ </para>
+
+ <para>
+ You need a Spotify premium account to use this plugin, and you need
+ to setup username and password in the configuration file. The
+ configuration settings are global since the despotify playlist plugin
+ use the same settings.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>despotify_user</varname>
+ </entry>
+ <entry>
+ Sets up the Spotify username (required)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>despotify_password</varname>
+ </entry>
+ <entry>
+ Sets up the Spotify password (required)
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>despotify_high_bitrate</varname>
+ </entry>
+ <entry>
+ Set up if high bitrate should be used for Spotify tunes.
+ High bitrate sounds better but slow systems can have problems
+ with playback (default yes).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
+ <title><varname>soup</varname></title>
+
+ <para>
+ Opens remote files or streams over HTTP.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>proxy</varname>
+ </entry>
+ <entry>
+ Sets the address of the HTTP proxy server.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
</section>
<section>
@@ -658,6 +768,178 @@ cd mpd-version</programlisting>
</section>
<section>
+ <title>Encoder plugins</title>
+
+ <section>
+ <title><varname>flac</varname></title>
+
+ <para>
+ Encodes into FLAC (lossless).
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>compression</varname>
+ </entry>
+ <entry>
+ Sets the <filename>libFLAC</filename> compression
+ level. The levels range from 0 (fastest, least
+ compression) to 8 (slowest, most compression).
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
+ <title><varname>lame</varname></title>
+
+ <para>
+ Encodes into MP3 using the LAME library.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>quality</varname>
+ </entry>
+ <entry>
+ Sets the quality for VBR. 0 is the highest quality,
+ 9 is the lowest quality. Cannot be used with
+ <varname>bitrate</varname>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>bitrate</varname>
+ </entry>
+ <entry>
+ Sets the bit rate in kilobit per second. Cannot be
+ used with <varname>quality</varname>.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
+ <title><varname>null</varname></title>
+
+ <para>
+ Does not encode anything, passes the input PCM data as-is.
+ </para>
+ </section>
+
+ <section>
+ <title><varname>twolame</varname></title>
+
+ <para>
+ Encodes into MP2 using the <filename>twolame</filename>
+ library.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>quality</varname>
+ </entry>
+ <entry>
+ Sets the quality for VBR. 0 is the highest quality,
+ 9 is the lowest quality. Cannot be used with
+ <varname>bitrate</varname>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>bitrate</varname>
+ </entry>
+ <entry>
+ Sets the bit rate in kilobit per second. Cannot be
+ used with <varname>quality</varname>.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
+ <title><varname>vorbis</varname></title>
+
+ <para>
+ Encodes into Ogg Vorbis.
+ </para>
+
+ <informaltable>
+ <tgroup cols="2">
+ <thead>
+ <row>
+ <entry>Setting</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry>
+ <varname>quality</varname>
+ </entry>
+ <entry>
+ Sets the quality for VBR. -1 is the lowest quality,
+ 10 is the highest quality. Cannot be used with
+ <varname>bitrate</varname>.
+ </entry>
+ </row>
+ <row>
+ <entry>
+ <varname>bitrate</varname>
+ </entry>
+ <entry>
+ Sets the bit rate in kilobit per second. Cannot be
+ used with <varname>quality</varname>.
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+ </section>
+
+ <section>
+ <title><varname>wave</varname></title>
+
+ <para>
+ Encodes into WAV (lossless).
+ </para>
+ </section>
+ </section>
+
+ <section>
<title>Output plugins</title>
<section>
@@ -1366,6 +1648,15 @@ cd mpd-version</programlisting>
</row>
<row>
<entry>
+ <varname>url</varname>
+ <parameter>URL</parameter>
+ </entry>
+ <entry>
+ Sets a URL associated with the stream (optional).
+ </entry>
+ </row>
+ <row>
+ <entry>
<varname>public</varname>
<parameter>yes|no</parameter>
</entry>
@@ -1498,6 +1789,27 @@ cd mpd-version</programlisting>
playlist files.
</para>
</section>
+
+ <section>
+ <title><varname>despotify</varname></title>
+
+ <para>
+ Adds <ulink url="http://www.spotify.com/">Spotify</ulink>
+ playlists. Spotify playlists use the <filename>spt://</filename> URI,
+ and a Spotify playlist URL. So for example, you can load a playlist
+ with
+ </para>
+
+ <para>
+ <filename>mpc load spt://spotify:user:simon.kagstrom:playlist:3SUwkOe5VbVHysZcidEZtH</filename>
+ </para>
+
+ <para>
+ See the despotify input plugin for configuration options (username
+ and password needs to be setup)
+ </para>
+ </section>
+
</section>
</chapter>
</book>
diff --git a/src/ack.h b/src/ack.h
index af3d1be9a..f86b605f7 100644
--- a/src/ack.h
+++ b/src/ack.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/aiff.c b/src/aiff.c
index e2ca0dfe4..35b716eef 100644
--- a/src/aiff.c
+++ b/src/aiff.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/aiff.h b/src/aiff.h
index 52c0a73ec..a0ae2d41a 100644
--- a/src/aiff.h
+++ b/src/aiff.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/ape.c b/src/ape.c
index 5f4da3f2e..6257fe6b3 100644
--- a/src/ape.c
+++ b/src/ape.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/ape.h b/src/ape.h
index 754b9bb2d..c2b271b15 100644
--- a/src/ape.h
+++ b/src/ape.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive/bz2_archive_plugin.c b/src/archive/bz2_archive_plugin.c
index 2414eb519..90418720e 100644
--- a/src/archive/bz2_archive_plugin.c
+++ b/src/archive/bz2_archive_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include "config.h"
#include "archive/bz2_archive_plugin.h"
#include "archive_api.h"
+#include "input_internal.h"
#include "input_plugin.h"
#include "refcount.h"
diff --git a/src/archive/bz2_archive_plugin.h b/src/archive/bz2_archive_plugin.h
index 199049008..46c69a66c 100644
--- a/src/archive/bz2_archive_plugin.h
+++ b/src/archive/bz2_archive_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive/iso9660_archive_plugin.c b/src/archive/iso9660_archive_plugin.c
index 142fa10e0..da55feca2 100644
--- a/src/archive/iso9660_archive_plugin.c
+++ b/src/archive/iso9660_archive_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include "config.h"
#include "archive/iso9660_archive_plugin.h"
#include "archive_api.h"
+#include "input_internal.h"
#include "input_plugin.h"
#include "refcount.h"
diff --git a/src/archive/iso9660_archive_plugin.h b/src/archive/iso9660_archive_plugin.h
index 2a3864cee..47dc6e474 100644
--- a/src/archive/iso9660_archive_plugin.h
+++ b/src/archive/iso9660_archive_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive/zzip_archive_plugin.c b/src/archive/zzip_archive_plugin.c
index 3c2b80318..829fb6dd1 100644
--- a/src/archive/zzip_archive_plugin.c
+++ b/src/archive/zzip_archive_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,7 @@
#include "archive/zzip_archive_plugin.h"
#include "archive_api.h"
#include "archive_api.h"
+#include "input_internal.h"
#include "input_plugin.h"
#include "refcount.h"
diff --git a/src/archive/zzip_archive_plugin.h b/src/archive/zzip_archive_plugin.h
index 6d5037eef..2b2c01e5a 100644
--- a/src/archive/zzip_archive_plugin.h
+++ b/src/archive/zzip_archive_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive_api.c b/src/archive_api.c
index b15810f1b..be3c35f7e 100644
--- a/src/archive_api.c
+++ b/src/archive_api.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive_api.h b/src/archive_api.h
index f08960c72..4e0f603f5 100644
--- a/src/archive_api.h
+++ b/src/archive_api.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive_internal.h b/src/archive_internal.h
index 03439e826..0d885e91c 100644
--- a/src/archive_internal.h
+++ b/src/archive_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive_list.c b/src/archive_list.c
index 2656726b5..24aa060c9 100644
--- a/src/archive_list.c
+++ b/src/archive_list.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,7 @@
#include "config.h"
#include "archive_list.h"
#include "archive_plugin.h"
-#include "utils.h"
+#include "string_util.h"
#include "archive/bz2_archive_plugin.h"
#include "archive/iso9660_archive_plugin.h"
#include "archive/zzip_archive_plugin.h"
diff --git a/src/archive_list.h b/src/archive_list.h
index b65245ce9..24e4063bf 100644
--- a/src/archive_list.h
+++ b/src/archive_list.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive_plugin.c b/src/archive_plugin.c
index 60da4d283..e73035053 100644
--- a/src/archive_plugin.c
+++ b/src/archive_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/archive_plugin.h b/src/archive_plugin.h
index b08c93389..7f038486b 100644
--- a/src/archive_plugin.h
+++ b/src/archive_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -68,7 +68,7 @@ struct archive_plugin {
* Opens an input_stream of a file within the archive.
*
* @param path the path within the archive
- * @param error_r location to store the error occuring, or
+ * @param error_r location to store the error occurring, or
* NULL to ignore errors
*/
struct input_stream *(*open_stream)(struct archive_file *af,
diff --git a/src/audio.c b/src/audio.c
index f9894cf3c..22879fc79 100644
--- a/src/audio.c
+++ b/src/audio.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/audio.h b/src/audio.h
index cb3ab7bbe..61835d4d7 100644
--- a/src/audio.h
+++ b/src/audio.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/audio_check.c b/src/audio_check.c
index 61d2c5833..a9aa2dd82 100644
--- a/src/audio_check.c
+++ b/src/audio_check.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/audio_check.h b/src/audio_check.h
index 4862e7f15..1d0bc1ded 100644
--- a/src/audio_check.h
+++ b/src/audio_check.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/audio_format.c b/src/audio_format.c
index 13403fbc1..799e0dd89 100644
--- a/src/audio_format.c
+++ b/src/audio_format.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/audio_format.h b/src/audio_format.h
index a4450ad71..1a54a092a 100644
--- a/src/audio_format.h
+++ b/src/audio_format.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/audio_parser.c b/src/audio_parser.c
index 139cf1c04..80bf9a5d7 100644
--- a/src/audio_parser.c
+++ b/src/audio_parser.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/audio_parser.h b/src/audio_parser.h
index 214ec5eb1..a963eb467 100644
--- a/src/audio_parser.h
+++ b/src/audio_parser.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -38,7 +38,7 @@ struct audio_format;
* @param dest the destination #audio_format struct
* @param src the input string
* @param mask if true, then "*" is allowed for any number of items
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
* @return true on success
*/
diff --git a/src/buffer.c b/src/buffer.c
index bee871700..559f39a9a 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/buffer.h b/src/buffer.h
index 75e5bc6e6..f860231e7 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/check.h b/src/check.h
index 56061621f..0642a4b91 100644
--- a/src/check.h
+++ b/src/check.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/chunk.c b/src/chunk.c
index 79597506d..1eb96f4b9 100644
--- a/src/chunk.c
+++ b/src/chunk.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/chunk.h b/src/chunk.h
index 02e7b3650..a06a203eb 100644
--- a/src/chunk.h
+++ b/src/chunk.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client.c b/src/client.c
index 9668c9249..3fa2c9be4 100644
--- a/src/client.c
+++ b/src/client.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client.h b/src/client.h
index d46747b4f..5647e5eae 100644
--- a/src/client.h
+++ b/src/client.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -27,11 +27,13 @@
struct client;
struct sockaddr;
+struct player_control;
void client_manager_init(void);
void client_manager_deinit(void);
-void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid);
+void client_new(struct player_control *player_control,
+ int fd, const struct sockaddr *sa, size_t sa_length, int uid);
bool client_is_expired(const struct client *client);
@@ -60,17 +62,4 @@ void client_vprintf(struct client *client, const char *fmt, va_list args);
*/
G_GNUC_PRINTF(2, 3) void client_printf(struct client *client, const char *fmt, ...);
-/**
- * Adds the specified idle flags to all clients and immediately sends
- * notifications to all waiting clients.
- */
-void client_manager_idle_add(unsigned flags);
-
-/**
- * Checks whether the client has pending idle flags. If yes, they are
- * sent immediately and "true" is returned". If no, it puts the
- * client into waiting mode and returns false.
- */
-bool client_idle_wait(struct client *client, unsigned flags);
-
#endif
diff --git a/src/client_event.c b/src/client_event.c
index 93f5a9df7..4f54ae0a7 100644
--- a/src/client_event.c
+++ b/src/client_event.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client_expire.c b/src/client_expire.c
index a5b0be047..1ca32ebcc 100644
--- a/src/client_expire.c
+++ b/src/client_expire.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client_global.c b/src/client_global.c
index fc5adedba..adf3b2f9e 100644
--- a/src/client_global.c
+++ b/src/client_global.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client_idle.c b/src/client_idle.c
index 10be4d430..930911d6e 100644
--- a/src/client_idle.c
+++ b/src/client_idle.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
*/
#include "config.h"
+#include "client_idle.h"
#include "client_internal.h"
#include "idle.h"
@@ -50,12 +51,9 @@ client_idle_notify(struct client *client)
g_timer_start(client->last_activity);
}
-static void
-client_idle_callback(gpointer data, gpointer user_data)
+void
+client_idle_add(struct client *client, unsigned flags)
{
- struct client *client = data;
- unsigned flags = GPOINTER_TO_UINT(user_data);
-
if (client_is_expired(client))
return;
@@ -67,6 +65,15 @@ client_idle_callback(gpointer data, gpointer user_data)
}
}
+static void
+client_idle_callback(gpointer data, gpointer user_data)
+{
+ struct client *client = data;
+ unsigned flags = GPOINTER_TO_UINT(user_data);
+
+ client_idle_add(client, flags);
+}
+
void client_manager_idle_add(unsigned flags)
{
assert(flags != 0);
diff --git a/src/client_idle.h b/src/client_idle.h
new file mode 100644
index 000000000..c56fd014c
--- /dev/null
+++ b/src/client_idle.h
@@ -0,0 +1,45 @@
+/*
+ * 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_CLIENT_IDLE_H
+#define MPD_CLIENT_IDLE_H
+
+#include <stdbool.h>
+
+struct client;
+
+void
+client_idle_add(struct client *client, unsigned flags);
+
+/**
+ * Adds the specified idle flags to all clients and immediately sends
+ * notifications to all waiting clients.
+ */
+void
+client_manager_idle_add(unsigned flags);
+
+/**
+ * Checks whether the client has pending idle flags. If yes, they are
+ * sent immediately and "true" is returned". If no, it puts the
+ * client into waiting mode and returns false.
+ */
+bool
+client_idle_wait(struct client *client, unsigned flags);
+
+#endif
diff --git a/src/client_internal.h b/src/client_internal.h
index 2b1b92433..ba97e4b8f 100644
--- a/src/client_internal.h
+++ b/src/client_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -21,17 +21,25 @@
#define MPD_CLIENT_INTERNAL_H
#include "client.h"
+#include "client_message.h"
#include "command.h"
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "client"
+enum {
+ CLIENT_MAX_SUBSCRIPTIONS = 16,
+ CLIENT_MAX_MESSAGES = 64,
+};
+
struct deferred_buffer {
size_t size;
char data[sizeof(long)];
};
struct client {
+ struct player_control *player_control;
+
GIOChannel *channel;
guint source_id;
@@ -67,6 +75,28 @@ struct client {
/** idle flags that the client wants to receive */
unsigned idle_subscriptions;
+
+ /**
+ * A list of channel names this client is subscribed to.
+ */
+ GSList *subscriptions;
+
+ /**
+ * The number of subscriptions in #subscriptions. Used to
+ * limit the number of subscriptions.
+ */
+ unsigned num_subscriptions;
+
+ /**
+ * A list of messages this client has received in reverse
+ * order (latest first).
+ */
+ GSList *messages;
+
+ /**
+ * The number of messages in #messages.
+ */
+ unsigned num_messages;
};
extern unsigned int client_max_connections;
diff --git a/src/client_list.c b/src/client_list.c
index 5332ed65f..2c7f37aff 100644
--- a/src/client_list.c
+++ b/src/client_list.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client_message.c b/src/client_message.c
new file mode 100644
index 000000000..b681b4e7f
--- /dev/null
+++ b/src/client_message.c
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+
+#include "client_message.h"
+
+#include <assert.h>
+#include <glib.h>
+
+G_GNUC_PURE
+static bool
+valid_channel_char(const char ch)
+{
+ return g_ascii_isalnum(ch) ||
+ ch == '_' || ch == '-' || ch == '.' || ch == ':';
+}
+
+bool
+client_message_valid_channel_name(const char *name)
+{
+ do {
+ if (!valid_channel_char(*name))
+ return false;
+ } while (*++name != 0);
+
+ return true;
+}
+
+void
+client_message_init_null(struct client_message *msg)
+{
+ assert(msg != NULL);
+
+ msg->channel = NULL;
+ msg->message = NULL;
+}
+
+void
+client_message_init(struct client_message *msg,
+ const char *channel, const char *message)
+{
+ assert(msg != NULL);
+
+ msg->channel = g_strdup(channel);
+ msg->message = g_strdup(message);
+}
+
+void
+client_message_copy(struct client_message *dest,
+ const struct client_message *src)
+{
+ assert(dest != NULL);
+ assert(src != NULL);
+ assert(client_message_defined(src));
+
+ client_message_init(dest, src->channel, src->message);
+}
+
+struct client_message *
+client_message_dup(const struct client_message *src)
+{
+ struct client_message *dest = g_slice_new(struct client_message);
+ client_message_copy(dest, src);
+ return dest;
+}
+
+void
+client_message_deinit(struct client_message *msg)
+{
+ assert(msg != NULL);
+
+ g_free(msg->channel);
+ g_free(msg->message);
+}
+
+void
+client_message_free(struct client_message *msg)
+{
+ client_message_deinit(msg);
+ g_slice_free(struct client_message, msg);
+}
diff --git a/src/client_message.h b/src/client_message.h
new file mode 100644
index 000000000..5c7e86c15
--- /dev/null
+++ b/src/client_message.h
@@ -0,0 +1,72 @@
+/*
+ * 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_CLIENT_MESSAGE_H
+#define MPD_CLIENT_MESSAGE_H
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <glib.h>
+
+/**
+ * A client-to-client message.
+ */
+struct client_message {
+ char *channel;
+
+ char *message;
+};
+
+G_GNUC_PURE
+bool
+client_message_valid_channel_name(const char *name);
+
+G_GNUC_PURE
+static inline bool
+client_message_defined(const struct client_message *msg)
+{
+ assert(msg != NULL);
+ assert((msg->channel == NULL) == (msg->message == NULL));
+
+ return msg->channel != NULL;
+}
+
+void
+client_message_init_null(struct client_message *msg);
+
+void
+client_message_init(struct client_message *msg,
+ const char *channel, const char *message);
+
+void
+client_message_copy(struct client_message *dest,
+ const struct client_message *src);
+
+G_GNUC_MALLOC G_GNUC_PURE
+struct client_message *
+client_message_dup(const struct client_message *src);
+
+void
+client_message_deinit(struct client_message *msg);
+
+void
+client_message_free(struct client_message *msg);
+
+#endif
diff --git a/src/client_new.c b/src/client_new.c
index 781a36524..5b2dfde65 100644
--- a/src/client_new.c
+++ b/src/client_new.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -41,12 +41,15 @@
static const char GREETING[] = "OK MPD " PROTOCOL_VERSION "\n";
-void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
+void
+client_new(struct player_control *player_control,
+ int fd, const struct sockaddr *sa, size_t sa_length, int uid)
{
static unsigned int next_client_num;
struct client *client;
char *remote;
+ assert(player_control != NULL);
assert(fd >= 0);
#ifdef HAVE_LIBWRAP
@@ -81,6 +84,7 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
}
client = g_new0(struct client, 1);
+ client->player_control = player_control;
#ifndef G_OS_WIN32
client->channel = g_io_channel_unix_new(fd);
@@ -117,6 +121,10 @@ void client_new(int fd, const struct sockaddr *sa, size_t sa_length, int uid)
client->send_buf_used = 0;
+ client->subscriptions = NULL;
+ client->messages = NULL;
+ client->num_messages = 0;
+
(void)send(fd, GREETING, sizeof(GREETING) - 1, 0);
client_list_add(client);
diff --git a/src/client_process.c b/src/client_process.c
index aeb75bb57..57a8a7824 100644
--- a/src/client_process.c
+++ b/src/client_process.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client_read.c b/src/client_read.c
index 7a6bd3d5e..26ade264e 100644
--- a/src/client_read.c
+++ b/src/client_read.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/client_subscribe.c b/src/client_subscribe.c
new file mode 100644
index 000000000..c65a7ed31
--- /dev/null
+++ b/src/client_subscribe.c
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+
+#include "client_subscribe.h"
+#include "client_internal.h"
+#include "client_idle.h"
+#include "idle.h"
+
+#include <string.h>
+
+G_GNUC_PURE
+static GSList *
+client_find_subscription(const struct client *client, const char *channel)
+{
+ for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
+ if (strcmp((const char *)i->data, channel) == 0)
+ return i;
+
+ return NULL;
+}
+
+enum client_subscribe_result
+client_subscribe(struct client *client, const char *channel)
+{
+ assert(client != NULL);
+ assert(channel != NULL);
+
+ if (!client_message_valid_channel_name(channel))
+ return CLIENT_SUBSCRIBE_INVALID;
+
+ if (client_find_subscription(client, channel) != NULL)
+ return CLIENT_SUBSCRIBE_ALREADY;
+
+ if (client->num_subscriptions >= CLIENT_MAX_SUBSCRIPTIONS)
+ return CLIENT_SUBSCRIBE_FULL;
+
+ client->subscriptions = g_slist_prepend(client->subscriptions,
+ g_strdup(channel));
+ ++client->num_subscriptions;
+
+ idle_add(IDLE_SUBSCRIPTION);
+
+ return CLIENT_SUBSCRIBE_OK;
+}
+
+bool
+client_unsubscribe(struct client *client, const char *channel)
+{
+ GSList *i = client_find_subscription(client, channel);
+ if (i == NULL)
+ return false;
+
+ assert(client->num_subscriptions > 0);
+
+ client->subscriptions = g_slist_remove(client->subscriptions, i->data);
+ --client->num_subscriptions;
+
+ idle_add(IDLE_SUBSCRIPTION);
+
+ assert((client->num_subscriptions == 0) ==
+ (client->subscriptions == NULL));
+
+ return true;
+}
+
+void
+client_unsubscribe_all(struct client *client)
+{
+ for (GSList *i = client->subscriptions; i != NULL; i = g_slist_next(i))
+ g_free(i->data);
+
+ g_slist_free(client->subscriptions);
+ client->subscriptions = NULL;
+ client->num_subscriptions = 0;
+}
+
+bool
+client_push_message(struct client *client, const struct client_message *msg)
+{
+ assert(client != NULL);
+ assert(msg != NULL);
+ assert(client_message_defined(msg));
+
+ if (client->num_messages >= CLIENT_MAX_MESSAGES ||
+ client_find_subscription(client, msg->channel) == NULL)
+ return false;
+
+ if (client->messages == NULL)
+ client_idle_add(client, IDLE_MESSAGE);
+
+ client->messages = g_slist_prepend(client->messages,
+ client_message_dup(msg));
+ ++client->num_messages;
+
+ return true;
+}
+
+GSList *
+client_read_messages(struct client *client)
+{
+ GSList *messages = g_slist_reverse(client->messages);
+
+ client->messages = NULL;
+ client->num_messages = 0;
+
+ return messages;
+}
diff --git a/src/client_subscribe.h b/src/client_subscribe.h
new file mode 100644
index 000000000..09f864417
--- /dev/null
+++ b/src/client_subscribe.h
@@ -0,0 +1,59 @@
+/*
+ * 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_CLIENT_SUBSCRIBE_H
+#define MPD_CLIENT_SUBSCRIBE_H
+
+#include <stdbool.h>
+#include <glib.h>
+
+struct client;
+struct client_message;
+
+enum client_subscribe_result {
+ /** success */
+ CLIENT_SUBSCRIBE_OK,
+
+ /** invalid channel name */
+ CLIENT_SUBSCRIBE_INVALID,
+
+ /** already subscribed to this channel */
+ CLIENT_SUBSCRIBE_ALREADY,
+
+ /** too many subscriptions */
+ CLIENT_SUBSCRIBE_FULL,
+};
+
+enum client_subscribe_result
+client_subscribe(struct client *client, const char *channel);
+
+bool
+client_unsubscribe(struct client *client, const char *channel);
+
+void
+client_unsubscribe_all(struct client *client);
+
+bool
+client_push_message(struct client *client, const struct client_message *msg);
+
+G_GNUC_MALLOC
+GSList *
+client_read_messages(struct client *client);
+
+#endif
diff --git a/src/client_write.c b/src/client_write.c
index 543cdbb6c..78cfca8a1 100644
--- a/src/client_write.c
+++ b/src/client_write.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/cmdline.c b/src/cmdline.c
index 2c1db890b..45e361e71 100644
--- a/src/cmdline.c
+++ b/src/cmdline.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -79,7 +79,7 @@ static void version(void)
puts(PACKAGE " (MPD: Music Player Daemon) " VERSION " \n"
"\n"
"Copyright (C) 2003-2007 Warren Dukes <warren.dukes@gmail.com>\n"
- "Copyright (C) 2008-2010 Max Kellermann <max@duempel.org>\n"
+ "Copyright (C) 2008-2011 Max Kellermann <max@duempel.org>\n"
"This is free software; see the source for copying conditions. There is NO\n"
"warranty; not even MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
"\n"
diff --git a/src/cmdline.h b/src/cmdline.h
index b7af63c5a..68f625a6c 100644
--- a/src/cmdline.h
+++ b/src/cmdline.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/command.c b/src/command.c
index 64f161805..eb2b9ae4f 100644
--- a/src/command.c
+++ b/src/command.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,12 +24,12 @@
#include "playlist_print.h"
#include "playlist_save.h"
#include "playlist_queue.h"
+#include "playlist_error.h"
#include "queue_print.h"
#include "ls.h"
#include "uri.h"
#include "decoder_print.h"
#include "directory.h"
-#include "directory_print.h"
#include "database.h"
#include "update.h"
#include "volume.h"
@@ -42,8 +42,14 @@
#include "output_print.h"
#include "locate.h"
#include "dbUtils.h"
+#include "db_error.h"
+#include "db_print.h"
+#include "db_selection.h"
#include "tag.h"
#include "client.h"
+#include "client_idle.h"
+#include "client_internal.h"
+#include "client_subscribe.h"
#include "tag_print.h"
#include "path.h"
#include "replay_gain_config.h"
@@ -372,6 +378,46 @@ print_playlist_result(struct client *client,
return COMMAND_RETURN_ERROR;
}
+/**
+ * Send the GError to the client and free the GError.
+ */
+static enum command_return
+print_error(struct client *client, GError *error)
+{
+ assert(client != NULL);
+ assert(error != NULL);
+
+ g_warning("%s", error->message);
+
+ if (error->domain == playlist_quark()) {
+ enum playlist_result result = error->code;
+ g_error_free(error);
+ return print_playlist_result(client, result);
+ } else if (error->domain == db_quark()) {
+ switch ((enum db_error)error->code) {
+ case DB_DISABLED:
+ command_error(client, ACK_ERROR_NO_EXIST, "%s",
+ error->message);
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+
+ case DB_NOT_FOUND:
+ g_error_free(error);
+ command_error(client, ACK_ERROR_NO_EXIST, "Not found");
+ return COMMAND_RETURN_ERROR;
+ }
+ } else if (error->domain == g_file_error_quark()) {
+ command_error(client, ACK_ERROR_SYSTEM, "%s",
+ g_strerror(error->code));
+ g_error_free(error);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ g_error_free(error);
+ command_error(client, ACK_ERROR_UNKNOWN, "error");
+ return COMMAND_RETURN_ERROR;
+}
+
static void
print_spl_list(struct client *client, GPtrArray *list)
{
@@ -434,7 +480,7 @@ handle_play(struct client *client, int argc, char *argv[])
if (argc == 2 && !check_int(client, &song, argv[1], need_positive))
return COMMAND_RETURN_ERROR;
- result = playlist_play(&g_playlist, song);
+ result = playlist_play(&g_playlist, client->player_control, song);
return print_playlist_result(client, result);
}
@@ -447,7 +493,7 @@ handle_playid(struct client *client, int argc, char *argv[])
if (argc == 2 && !check_int(client, &id, argv[1], need_positive))
return COMMAND_RETURN_ERROR;
- result = playlist_play_id(&g_playlist, id);
+ result = playlist_play_id(&g_playlist, client->player_control, id);
return print_playlist_result(client, result);
}
@@ -455,7 +501,7 @@ static enum command_return
handle_stop(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- playlist_stop(&g_playlist);
+ playlist_stop(&g_playlist, client->player_control);
return COMMAND_RETURN_OK;
}
@@ -476,9 +522,9 @@ handle_pause(struct client *client,
if (!check_bool(client, &pause_flag, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_pause(pause_flag);
+ pc_set_pause(client->player_control, pause_flag);
} else
- pc_pause();
+ pc_pause(client->player_control);
return COMMAND_RETURN_OK;
}
@@ -493,7 +539,7 @@ handle_status(struct client *client,
char *error;
int song;
- pc_get_status(&player_status);
+ pc_get_status(client->player_control, &player_status);
switch (player_status.state) {
case PLAYER_STATE_STOP:
@@ -526,9 +572,9 @@ handle_status(struct client *client,
playlist_get_consume(&g_playlist),
playlist_get_version(&g_playlist),
playlist_get_length(&g_playlist),
- (int)(pc_get_cross_fade() + 0.5),
- pc_get_mixramp_db(),
- pc_get_mixramp_delay(),
+ (int)(pc_get_cross_fade(client->player_control) + 0.5),
+ pc_get_mixramp_db(client->player_control),
+ pc_get_mixramp_delay(client->player_control),
state);
song = playlist_get_current_song(&g_playlist);
@@ -561,7 +607,7 @@ handle_status(struct client *client,
updateJobId);
}
- error = pc_get_error_message();
+ error = pc_get_error_message(client->player_control);
if (error != NULL) {
client_printf(client,
COMMAND_STATUS_ERROR ": %s\n",
@@ -605,6 +651,7 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
result = PLAYLIST_RESULT_DENIED;
#else
result = playlist_append_file(&g_playlist,
+ client->player_control,
uri + 7, client_get_uid(client),
NULL);
#endif
@@ -618,18 +665,16 @@ handle_add(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- result = playlist_append_uri(&g_playlist, uri, NULL);
+ result = playlist_append_uri(&g_playlist,
+ client->player_control,
+ uri, NULL);
return print_playlist_result(client, result);
}
- result = addAllIn(uri);
- if (result == (enum playlist_result)-1) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
- return COMMAND_RETURN_ERROR;
- }
-
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return addAllIn(client->player_control, uri, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -643,7 +688,9 @@ handle_addid(struct client *client, int argc, char *argv[])
#ifdef WIN32
result = PLAYLIST_RESULT_DENIED;
#else
- result = playlist_append_file(&g_playlist, uri + 7,
+ result = playlist_append_file(&g_playlist,
+ client->player_control,
+ uri + 7,
client_get_uid(client),
&added_id);
#endif
@@ -654,7 +701,9 @@ handle_addid(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- result = playlist_append_uri(&g_playlist, uri, &added_id);
+ result = playlist_append_uri(&g_playlist,
+ client->player_control,
+ uri, &added_id);
}
if (result != PLAYLIST_RESULT_SUCCESS)
@@ -664,11 +713,13 @@ handle_addid(struct client *client, int argc, char *argv[])
int to;
if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, added_id, to);
+ result = playlist_move_id(&g_playlist, client->player_control,
+ added_id, to);
if (result != PLAYLIST_RESULT_SUCCESS) {
enum command_return ret =
print_playlist_result(client, result);
- playlist_delete_id(&g_playlist, added_id);
+ playlist_delete_id(&g_playlist, client->player_control,
+ added_id);
return ret;
}
}
@@ -686,7 +737,8 @@ handle_delete(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_range(client, &start, &end, argv[1], need_range))
return COMMAND_RETURN_ERROR;
- result = playlist_delete_range(&g_playlist, start, end);
+ result = playlist_delete_range(&g_playlist, client->player_control,
+ start, end);
return print_playlist_result(client, result);
}
@@ -699,7 +751,7 @@ handle_deleteid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &id, argv[1], need_positive))
return COMMAND_RETURN_ERROR;
- result = playlist_delete_id(&g_playlist, id);
+ result = playlist_delete_id(&g_playlist, client->player_control, id);
return print_playlist_result(client, result);
}
@@ -720,7 +772,7 @@ handle_shuffle(G_GNUC_UNUSED struct client *client,
argv[1], need_range))
return COMMAND_RETURN_ERROR;
- playlist_shuffle(&g_playlist, start, end);
+ playlist_shuffle(&g_playlist, client->player_control, start, end);
return COMMAND_RETURN_OK;
}
@@ -728,7 +780,7 @@ static enum command_return
handle_clear(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- playlist_clear(&g_playlist);
+ playlist_clear(&g_playlist, client->player_control);
return COMMAND_RETURN_OK;
}
@@ -747,12 +799,16 @@ handle_load(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
enum playlist_result result;
- result = playlist_open_into_queue(argv[1], &g_playlist);
+ result = playlist_open_into_queue(argv[1], &g_playlist,
+ client->player_control, true);
if (result != PLAYLIST_RESULT_NO_SUCH_LIST)
return print_playlist_result(client, result);
- result = playlist_load_spl(&g_playlist, argv[1]);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return playlist_load_spl(&g_playlist, client->player_control,
+ argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -761,15 +817,10 @@ handle_listplaylist(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (playlist_file_print(client, argv[1], false))
return COMMAND_RETURN_OK;
- bool ret;
-
- ret = spl_print(client, argv[1], false);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
+ GError *error = NULL;
+ return spl_print(client, argv[1], false, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -779,22 +830,16 @@ handle_listplaylistinfo(struct client *client,
if (playlist_file_print(client, argv[1], true))
return COMMAND_RETURN_OK;
- bool ret;
-
- ret = spl_print(client, argv[1], true);
- if (!ret) {
- command_error(client, ACK_ERROR_NO_EXIST, "No such playlist");
- return COMMAND_RETURN_ERROR;
- }
-
- return COMMAND_RETURN_OK;
+ GError *error = NULL;
+ return spl_print(client, argv[1], true, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
handle_lsinfo(struct client *client, int argc, char *argv[])
{
const char *uri;
- const struct directory *directory;
if (argc == 2)
uri = argv[1];
@@ -802,17 +847,15 @@ handle_lsinfo(struct client *client, int argc, char *argv[])
/* default is root directory */
uri = "";
- directory = db_get_directory(uri);
- if (directory == NULL) {
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory not found");
- return COMMAND_RETURN_ERROR;
- }
+ struct db_selection selection;
+ db_selection_init(&selection, uri, false);
- directory_print(client, directory);
+ GError *error = NULL;
+ if (!db_selection_print(client, &selection, true, &error))
+ return print_error(client, error);
if (isRootDirectory(uri)) {
- GPtrArray *list = spl_list();
+ GPtrArray *list = spl_list(NULL);
if (list != NULL) {
print_spl_list(client, list);
spl_list_free(list);
@@ -825,19 +868,19 @@ handle_lsinfo(struct client *client, int argc, char *argv[])
static enum command_return
handle_rm(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- enum playlist_result result;
-
- result = spl_delete(argv[1]);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_delete(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
handle_rename(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- enum playlist_result result;
-
- result = spl_rename(argv[1], argv[2]);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_rename(argv[1], argv[2], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -905,7 +948,6 @@ handle_playlistid(struct client *client, int argc, char *argv[])
static enum command_return
handle_find(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
@@ -917,10 +959,10 @@ handle_find(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = findSongsIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret = findSongsIn(client, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -930,7 +972,6 @@ handle_find(struct client *client, int argc, char *argv[])
static enum command_return
handle_findadd(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
if (list == NULL || list->length == 0) {
@@ -941,10 +982,11 @@ handle_findadd(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = findAddIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret =
+ findAddIn(client->player_control, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -954,7 +996,6 @@ handle_findadd(struct client *client, int argc, char *argv[])
static enum command_return
handle_search(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
@@ -966,10 +1007,10 @@ handle_search(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = searchForSongsIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret = searchForSongsIn(client, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -979,7 +1020,6 @@ handle_search(struct client *client, int argc, char *argv[])
static enum command_return
handle_count(struct client *client, int argc, char *argv[])
{
- int ret;
struct locate_item_list *list =
locate_item_list_parse(argv + 1, argc - 1);
@@ -991,10 +1031,11 @@ handle_count(struct client *client, int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- ret = searchStatsForSongsIn(client, NULL, list);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
+ GError *error = NULL;
+ enum command_return ret =
+ searchStatsForSongsIn(client, "", list, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(list);
@@ -1048,13 +1089,14 @@ handle_playlistdelete(struct client *client,
G_GNUC_UNUSED int argc, char *argv[]) {
char *playlist = argv[1];
int from;
- enum playlist_result result;
if (!check_int(client, &from, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = spl_remove_index(playlist, from);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_remove_index(playlist, from, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1062,15 +1104,16 @@ handle_playlistmove(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
char *playlist = argv[1];
int from, to;
- enum playlist_result result;
if (!check_int(client, &from, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
if (!check_int(client, &to, argv[3], check_integer, argv[3]))
return COMMAND_RETURN_ERROR;
- result = spl_move_index(playlist, from, to);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_move_index(playlist, from, to, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1141,7 +1184,7 @@ handle_next(G_GNUC_UNUSED struct client *client,
int single = g_playlist.queue.single;
g_playlist.queue.single = false;
- playlist_next(&g_playlist);
+ playlist_next(&g_playlist, client->player_control);
g_playlist.queue.single = single;
return COMMAND_RETURN_OK;
@@ -1151,25 +1194,84 @@ static enum command_return
handle_previous(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- playlist_previous(&g_playlist);
+ playlist_previous(&g_playlist, client->player_control);
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_prio(struct client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned start_position, end_position;
+ if (!check_range(client, &start_position, &end_position,
+ argv[i], need_range))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ playlist_set_priority(&g_playlist,
+ client->player_control,
+ start_position, end_position,
+ priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_prioid(struct client *client, int argc, char *argv[])
+{
+ unsigned priority;
+
+ if (!check_unsigned(client, &priority, argv[1]))
+ return COMMAND_RETURN_ERROR;
+
+ if (priority > 0xff) {
+ command_error(client, ACK_ERROR_ARG,
+ "Priority out of range: %s", argv[1]);
+ return COMMAND_RETURN_ERROR;
+ }
+
+ for (int i = 2; i < argc; ++i) {
+ unsigned song_id;
+ if (!check_unsigned(client, &song_id, argv[i]))
+ return COMMAND_RETURN_ERROR;
+
+ enum playlist_result result =
+ playlist_set_priority_id(&g_playlist,
+ client->player_control,
+ song_id, priority);
+ if (result != PLAYLIST_RESULT_SUCCESS)
+ return print_playlist_result(client, result);
+ }
+
return COMMAND_RETURN_OK;
}
static enum command_return
handle_listall(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- char *directory = NULL;
- int ret;
+ const char *directory = "";
if (argc == 2)
directory = argv[1];
- ret = printAllIn(client, directory);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
-
- return ret;
+ GError *error = NULL;
+ return printAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1210,7 +1312,7 @@ handle_repeat(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- playlist_set_repeat(&g_playlist, status);
+ playlist_set_repeat(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK;
}
@@ -1228,7 +1330,7 @@ handle_single(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- playlist_set_single(&g_playlist, status);
+ playlist_set_single(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK;
}
@@ -1264,7 +1366,7 @@ handle_random(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- playlist_set_random(&g_playlist, status);
+ playlist_set_random(&g_playlist, client->player_control, status);
return COMMAND_RETURN_OK;
}
@@ -1279,7 +1381,7 @@ static enum command_return
handle_clearerror(G_GNUC_UNUSED struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- pc_clear_error();
+ pc_clear_error(client->player_control);
return COMMAND_RETURN_OK;
}
@@ -1288,7 +1390,6 @@ handle_list(struct client *client, int argc, char *argv[])
{
struct locate_item_list *conditionals;
int tagType = locate_parse_type(argv[1]);
- int ret;
if (tagType < 0) {
command_error(client, ACK_ERROR_ARG, "\"%s\" is not known", argv[1]);
@@ -1325,14 +1426,14 @@ handle_list(struct client *client, int argc, char *argv[])
}
}
- ret = listAllUniqueTags(client, tagType, conditionals);
+ GError *error = NULL;
+ enum command_return ret =
+ listAllUniqueTags(client, tagType, conditionals, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
locate_item_list_free(conditionals);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
-
return ret;
}
@@ -1348,7 +1449,8 @@ handle_move(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_move_range(&g_playlist, start, end, to);
+ result = playlist_move_range(&g_playlist, client->player_control,
+ start, end, to);
return print_playlist_result(client, result);
}
@@ -1362,7 +1464,8 @@ handle_moveid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &to, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_move_id(&g_playlist, id, to);
+ result = playlist_move_id(&g_playlist, client->player_control,
+ id, to);
return print_playlist_result(client, result);
}
@@ -1376,7 +1479,8 @@ handle_swap(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &song2, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs(&g_playlist, song1, song2);
+ result = playlist_swap_songs(&g_playlist, client->player_control,
+ song1, song2);
return print_playlist_result(client, result);
}
@@ -1390,7 +1494,8 @@ handle_swapid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
if (!check_int(client, &id2, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_swap_songs_id(&g_playlist, id1, id2);
+ result = playlist_swap_songs_id(&g_playlist, client->player_control,
+ id1, id2);
return print_playlist_result(client, result);
}
@@ -1405,7 +1510,8 @@ handle_seek(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_seek_song(&g_playlist, song, seek_time);
+ result = playlist_seek_song(&g_playlist, client->player_control,
+ song, seek_time);
return print_playlist_result(client, result);
}
@@ -1420,25 +1526,23 @@ handle_seekid(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_int(client, &seek_time, argv[2], check_integer, argv[2]))
return COMMAND_RETURN_ERROR;
- result = playlist_seek_song_id(&g_playlist, id, seek_time);
+ result = playlist_seek_song_id(&g_playlist, client->player_control,
+ id, seek_time);
return print_playlist_result(client, result);
}
static enum command_return
handle_listallinfo(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- char *directory = NULL;
- int ret;
+ const char *directory = "";
if (argc == 2)
directory = argv[1];
- ret = printInfoForAllIn(client, directory);
- if (ret == -1)
- command_error(client, ACK_ERROR_NO_EXIST,
- "directory or file not found");
-
- return ret;
+ GError *error = NULL;
+ return printInfoForAllIn(client, directory, &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1470,7 +1574,7 @@ handle_crossfade(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_unsigned(client, &xfade_time, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_cross_fade(xfade_time);
+ pc_set_cross_fade(client->player_control, xfade_time);
return COMMAND_RETURN_OK;
}
@@ -1482,7 +1586,7 @@ handle_mixrampdb(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_float(client, &db, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_mixramp_db(db);
+ pc_set_mixramp_db(client->player_control, db);
return COMMAND_RETURN_OK;
}
@@ -1494,7 +1598,7 @@ handle_mixrampdelay(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
if (!check_float(client, &delay_secs, argv[1]))
return COMMAND_RETURN_ERROR;
- pc_set_mixramp_delay(delay_secs);
+ pc_set_mixramp_delay(client->player_control, delay_secs);
return COMMAND_RETURN_OK;
}
@@ -1558,10 +1662,10 @@ handle_not_commands(struct client *client,
static enum command_return
handle_playlistclear(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
- enum playlist_result result;
-
- result = spl_clear(argv[1]);
- return print_playlist_result(client, result);
+ GError *error = NULL;
+ return spl_clear(argv[1], &error)
+ ? COMMAND_RETURN_OK
+ : print_error(client, error);
}
static enum command_return
@@ -1569,8 +1673,9 @@ handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
{
char *playlist = argv[1];
char *uri = argv[2];
- enum playlist_result result;
+ bool success;
+ GError *error = NULL;
if (uri_has_scheme(uri)) {
if (!uri_supported_scheme(uri)) {
command_error(client, ACK_ERROR_NO_EXIST,
@@ -1578,29 +1683,27 @@ handle_playlistadd(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
return COMMAND_RETURN_ERROR;
}
- result = spl_append_uri(uri, playlist);
+ success = spl_append_uri(argv[1], playlist, &error);
} else
- result = addAllInToStoredPlaylist(uri, playlist);
+ success = addAllInToStoredPlaylist(uri, playlist, &error);
- if (result == (enum playlist_result)-1) {
+ if (!success && error == NULL) {
command_error(client, ACK_ERROR_NO_EXIST,
"directory or file not found");
return COMMAND_RETURN_ERROR;
}
- return print_playlist_result(client, result);
+ return success ? COMMAND_RETURN_OK : print_error(client, error);
}
static enum command_return
handle_listplaylists(struct client *client,
G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
{
- GPtrArray *list = spl_list();
- if (list == NULL) {
- command_error(client, ACK_ERROR_SYSTEM,
- "failed to get list of stored playlists");
- return COMMAND_RETURN_ERROR;
- }
+ GError *error = NULL;
+ GPtrArray *list = spl_list(&error);
+ if (list == NULL)
+ return print_error(client, error);
print_spl_list(client, list);
spl_list_free(list);
@@ -1817,6 +1920,172 @@ handle_sticker(struct client *client, int argc, char *argv[])
}
#endif
+static enum command_return
+handle_subscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ switch (client_subscribe(client, argv[1])) {
+ case CLIENT_SUBSCRIBE_OK:
+ return COMMAND_RETURN_OK;
+
+ case CLIENT_SUBSCRIBE_INVALID:
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_ALREADY:
+ command_error(client, ACK_ERROR_EXIST,
+ "already subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+
+ case CLIENT_SUBSCRIBE_FULL:
+ command_error(client, ACK_ERROR_EXIST,
+ "subscription list is full");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ /* unreachable */
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_unsubscribe(struct client *client, G_GNUC_UNUSED int argc, char *argv[])
+{
+ assert(argc == 2);
+
+ if (client_unsubscribe(client, argv[1]))
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "not subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
+struct channels_context {
+ GStringChunk *chunk;
+
+ GHashTable *channels;
+};
+
+static void
+collect_channels(gpointer data, gpointer user_data)
+{
+ struct channels_context *context = user_data;
+ const struct client *client = data;
+
+ for (GSList *i = client->subscriptions; i != NULL;
+ i = g_slist_next(i)) {
+ const char *channel = i->data;
+
+ if (g_hash_table_lookup(context->channels, channel) == NULL) {
+ char *channel2 = g_string_chunk_insert(context->chunk,
+ channel);
+ g_hash_table_insert(context->channels, channel2,
+ context);
+ }
+ }
+}
+
+static void
+print_channel(gpointer key, G_GNUC_UNUSED gpointer value, gpointer user_data)
+{
+ struct client *client = user_data;
+ const char *channel = key;
+
+ client_printf(client, "channel: %s\n", channel);
+}
+
+static enum command_return
+handle_channels(struct client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ struct channels_context context = {
+ .chunk = g_string_chunk_new(1024),
+ .channels = g_hash_table_new(g_str_hash, g_str_equal),
+ };
+
+ client_list_foreach(collect_channels, &context);
+
+ g_hash_table_foreach(context.channels, print_channel, client);
+
+ g_hash_table_destroy(context.channels);
+ g_string_chunk_free(context.chunk);
+
+ return COMMAND_RETURN_OK;
+}
+
+static enum command_return
+handle_read_messages(struct client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 1);
+
+ GSList *messages = client_read_messages(client);
+
+ for (GSList *i = messages; i != NULL; i = g_slist_next(i)) {
+ struct client_message *msg = i->data;
+
+ client_printf(client, "channel: %s\nmessage: %s\n",
+ msg->channel, msg->message);
+ client_message_free(msg);
+ }
+
+ g_slist_free(messages);
+
+ return COMMAND_RETURN_OK;
+}
+
+struct send_message_context {
+ struct client_message msg;
+
+ bool sent;
+};
+
+static void
+send_message(gpointer data, gpointer user_data)
+{
+ struct send_message_context *context = user_data;
+ struct client *client = data;
+
+ if (client_push_message(client, &context->msg))
+ context->sent = true;
+}
+
+static enum command_return
+handle_send_message(struct client *client,
+ G_GNUC_UNUSED int argc, G_GNUC_UNUSED char *argv[])
+{
+ assert(argc == 3);
+
+ if (!client_message_valid_channel_name(argv[1])) {
+ command_error(client, ACK_ERROR_ARG,
+ "invalid channel name");
+ return COMMAND_RETURN_ERROR;
+ }
+
+ struct send_message_context context = {
+ .sent = false,
+ };
+
+ client_message_init(&context.msg, argv[1], argv[2]);
+
+ client_list_foreach(send_message, &context);
+
+ client_message_deinit(&context.msg);
+
+ if (context.sent)
+ return COMMAND_RETURN_OK;
+ else {
+ command_error(client, ACK_ERROR_NO_EXIST,
+ "nobody is subscribed to this channel");
+ return COMMAND_RETURN_ERROR;
+ }
+}
+
/**
* The command registry.
*
@@ -1825,6 +2094,7 @@ handle_sticker(struct client *client, int argc, char *argv[])
static const struct command commands[] = {
{ "add", PERMISSION_ADD, 1, 1, handle_add },
{ "addid", PERMISSION_ADD, 1, 2, handle_addid },
+ { "channels", PERMISSION_READ, 0, 0, handle_channels },
{ "clear", PERMISSION_CONTROL, 0, 0, handle_clear },
{ "clearerror", PERMISSION_CONTROL, 0, 0, handle_clearerror },
{ "close", PERMISSION_NONE, -1, -1, handle_close },
@@ -1874,19 +2144,23 @@ static const struct command commands[] = {
{ "plchanges", PERMISSION_READ, 1, 1, handle_plchanges },
{ "plchangesposid", PERMISSION_READ, 1, 1, handle_plchangesposid },
{ "previous", PERMISSION_CONTROL, 0, 0, handle_previous },
+ { "prio", PERMISSION_CONTROL, 2, -1, handle_prio },
+ { "prioid", PERMISSION_CONTROL, 2, -1, handle_prioid },
{ "random", PERMISSION_CONTROL, 1, 1, handle_random },
+ { "readmessages", PERMISSION_READ, 0, 0, handle_read_messages },
{ "rename", PERMISSION_CONTROL, 2, 2, handle_rename },
{ "repeat", PERMISSION_CONTROL, 1, 1, handle_repeat },
{ "replay_gain_mode", PERMISSION_CONTROL, 1, 1,
handle_replay_gain_mode },
{ "replay_gain_status", PERMISSION_READ, 0, 0,
handle_replay_gain_status },
- { "rescan", PERMISSION_ADMIN, 0, 1, handle_rescan },
+ { "rescan", PERMISSION_CONTROL, 0, 1, handle_rescan },
{ "rm", PERMISSION_CONTROL, 1, 1, handle_rm },
{ "save", PERMISSION_CONTROL, 1, 1, handle_save },
{ "search", PERMISSION_READ, 2, -1, handle_search },
{ "seek", PERMISSION_CONTROL, 2, 2, handle_seek },
{ "seekid", PERMISSION_CONTROL, 2, 2, handle_seekid },
+ { "sendmessage", PERMISSION_CONTROL, 2, 2, handle_send_message },
{ "setvol", PERMISSION_CONTROL, 1, 1, handle_setvol },
{ "shuffle", PERMISSION_CONTROL, 0, 1, handle_shuffle },
{ "single", PERMISSION_CONTROL, 1, 1, handle_single },
@@ -1896,10 +2170,12 @@ static const struct command commands[] = {
{ "sticker", PERMISSION_ADMIN, 3, -1, handle_sticker },
#endif
{ "stop", PERMISSION_CONTROL, 0, 0, handle_stop },
+ { "subscribe", PERMISSION_READ, 1, 1, handle_subscribe },
{ "swap", PERMISSION_CONTROL, 2, 2, handle_swap },
{ "swapid", PERMISSION_CONTROL, 2, 2, handle_swapid },
{ "tagtypes", PERMISSION_READ, 0, 0, handle_tagtypes },
- { "update", PERMISSION_ADMIN, 0, 1, handle_update },
+ { "unsubscribe", PERMISSION_READ, 1, 1, handle_unsubscribe },
+ { "update", PERMISSION_CONTROL, 0, 1, handle_update },
{ "urlhandlers", PERMISSION_READ, 0, 0, handle_urlhandlers },
};
diff --git a/src/command.h b/src/command.h
index 39389385d..68d1f95e4 100644
--- a/src/command.h
+++ b/src/command.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/conf.c b/src/conf.c
index 14dac93a6..66d8b2643 100644
--- a/src/conf.c
+++ b/src/conf.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
#include "config.h"
#include "conf.h"
#include "utils.h"
+#include "string_util.h"
#include "tokenizer.h"
#include "path.h"
#include "glib_compat.h"
@@ -58,6 +59,7 @@ static struct config_entry config_entries[] = {
{ .name = CONF_LOG_FILE, false, false },
{ .name = CONF_PID_FILE, false, false },
{ .name = CONF_STATE_FILE, false, false },
+ { .name = "restore_paused", false, false },
{ .name = CONF_USER, false, false },
{ .name = CONF_GROUP, false, false },
{ .name = CONF_BIND_TO_ADDRESS, true, false },
@@ -97,6 +99,9 @@ static struct config_entry config_entries[] = {
{ .name = CONF_PLAYLIST_PLUGIN, true, true },
{ .name = CONF_AUTO_UPDATE, false, false },
{ .name = CONF_AUTO_UPDATE_DEPTH, false, false },
+ { .name = CONF_DESPOTIFY_USER, false, false },
+ { .name = CONF_DESPOTIFY_PASSWORD, false, false},
+ { .name = CONF_DESPOTIFY_HIGH_BITRATE, false, false },
{ .name = "filter", true, true },
};
@@ -138,7 +143,7 @@ config_new_param(const char *value, int line)
return ret;
}
-static void
+void
config_param_free(struct config_param *param)
{
g_free(param->value);
@@ -218,20 +223,13 @@ void config_global_check(void)
}
}
-bool
+void
config_add_block_param(struct config_param * param, const char *name,
- const char *value, int line, GError **error_r)
+ const char *value, int line)
{
struct block_param *bp;
- bp = config_get_block_param(param, name);
- if (bp != NULL) {
- g_set_error(error_r, config_quark(), 0,
- "\"%s\" first defined on line %i, and "
- "redefined on line %i\n", name,
- bp->line, line);
- return false;
- }
+ assert(config_get_block_param(param, name) == NULL);
param->num_block_params++;
@@ -245,7 +243,46 @@ config_add_block_param(struct config_param * param, const char *name,
bp->value = g_strdup(value);
bp->line = line;
bp->used = false;
+}
+static bool
+config_read_name_value(struct config_param *param, char *input, unsigned line,
+ GError **error_r)
+{
+ const char *name = tokenizer_next_word(&input, error_r);
+ if (name == NULL) {
+ assert(*input != 0);
+ return false;
+ }
+
+ const char *value = tokenizer_next_string(&input, error_r);
+ if (value == NULL) {
+ if (*input == 0) {
+ assert(error_r == NULL || *error_r == NULL);
+ g_set_error(error_r, config_quark(), 0,
+ "Value missing");
+ } else {
+ assert(error_r == NULL || *error_r != NULL);
+ }
+
+ return false;
+ }
+
+ if (*input != 0 && *input != CONF_COMMENT) {
+ g_set_error(error_r, config_quark(), 0,
+ "Unknown tokens after value");
+ return false;
+ }
+
+ const struct block_param *bp = config_get_block_param(param, name);
+ if (bp != NULL) {
+ g_set_error(error_r, config_quark(), 0,
+ "\"%s\" is duplicate, first defined on line %i",
+ name, bp->line);
+ return false;
+ }
+
+ config_add_block_param(param, name, value, line);
return true;
}
@@ -254,11 +291,9 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
{
struct config_param *ret = config_new_param(NULL, *count);
GError *error = NULL;
- bool success;
while (true) {
char *line;
- const char *name, *value;
line = fgets(string, MAX_STRING_SIZE, fp);
if (line == NULL) {
@@ -269,7 +304,7 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
}
(*count)++;
- line = g_strchug(line);
+ line = strchug_fast(line);
if (*line == 0 || *line == CONF_COMMENT)
continue;
@@ -277,7 +312,7 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
/* end of this block; return from the function
(and from this "while" loop) */
- line = g_strchug(line + 1);
+ line = strchug_fast(line + 1);
if (*line != 0 && *line != CONF_COMMENT) {
config_param_free(ret);
g_set_error(error_r, config_quark(), 0,
@@ -291,42 +326,13 @@ config_read_block(FILE *fp, int *count, char *string, GError **error_r)
/* parse name and value */
- name = tokenizer_next_word(&line, &error);
- if (name == NULL) {
+ if (!config_read_name_value(ret, line, *count, &error)) {
assert(*line != 0);
config_param_free(ret);
g_propagate_prefixed_error(error_r, error,
"line %i: ", *count);
return NULL;
}
-
- value = tokenizer_next_string(&line, &error);
- if (value == NULL) {
- config_param_free(ret);
- if (*line == 0)
- g_set_error(error_r, config_quark(), 0,
- "line %i: Value missing", *count);
- else
- g_propagate_prefixed_error(error_r, error,
- "line %i: ",
- *count);
- return NULL;
- }
-
- if (*line != 0 && *line != CONF_COMMENT) {
- config_param_free(ret);
- g_set_error(error_r, config_quark(), 0,
- "line %i: Unknown tokens after value",
- *count);
- return NULL;
- }
-
- success = config_add_block_param(ret, name, value, *count,
- error_r);
- if (!success) {
- config_param_free(ret);
- return false;
- }
}
}
@@ -355,7 +361,7 @@ config_read_file(const char *file, GError **error_r)
count++;
- line = g_strchug(string);
+ line = strchug_fast(string);
if (*line == 0 || *line == CONF_COMMENT)
continue;
@@ -405,7 +411,7 @@ config_read_file(const char *file, GError **error_r)
return false;
}
- line = g_strchug(line + 1);
+ line = strchug_fast(line + 1);
if (*line != 0 && *line != CONF_COMMENT) {
g_set_error(error_r, config_quark(), 0,
"line %i: Unknown tokens after '{'",
@@ -457,7 +463,7 @@ config_read_file(const char *file, GError **error_r)
return true;
}
-struct config_param *
+const struct config_param *
config_get_next_param(const char *name, const struct config_param * last)
{
struct config_entry *entry;
@@ -497,22 +503,23 @@ config_get_string(const char *name, const char *default_value)
return param->value;
}
-const char *
-config_get_path(const char *name)
+char *
+config_dup_path(const char *name, GError **error_r)
{
- struct config_param *param = config_get_param(name);
- char *path;
+ assert(error_r != NULL);
+ assert(*error_r == NULL);
+ const struct config_param *param = config_get_param(name);
if (param == NULL)
return NULL;
- path = parsePath(param->value);
- if (path == NULL)
- MPD_ERROR("error parsing \"%s\" at line %i\n",
- name, param->line);
+ char *path = parsePath(param->value, error_r);
+ if (G_UNLIKELY(path == NULL))
+ g_prefix_error(error_r,
+ "Invalid path in \"%s\" at line %i: ",
+ name, param->line);
- g_free(param->value);
- return param->value = path;
+ return path;
}
unsigned
@@ -553,7 +560,7 @@ config_get_positive(const char *name, unsigned default_value)
return (unsigned)value;
}
-struct block_param *
+const struct block_param *
config_get_block_param(const struct config_param * param, const char *name)
{
if (param == NULL)
@@ -591,7 +598,7 @@ const char *
config_get_block_string(const struct config_param *param, const char *name,
const char *default_value)
{
- struct block_param *bp = config_get_block_param(param, name);
+ const struct block_param *bp = config_get_block_param(param, name);
if (bp == NULL)
return default_value;
@@ -599,11 +606,31 @@ config_get_block_string(const struct config_param *param, const char *name,
return bp->value;
}
+char *
+config_dup_block_path(const struct config_param *param, const char *name,
+ GError **error_r)
+{
+ assert(error_r != NULL);
+ assert(*error_r == NULL);
+
+ const struct block_param *bp = config_get_block_param(param, name);
+ if (bp == NULL)
+ return NULL;
+
+ char *path = parsePath(bp->value, error_r);
+ if (G_UNLIKELY(path == NULL))
+ g_prefix_error(error_r,
+ "Invalid path in \"%s\" at line %i: ",
+ name, bp->line);
+
+ return path;
+}
+
unsigned
config_get_block_unsigned(const struct config_param *param, const char *name,
unsigned default_value)
{
- struct block_param *bp = config_get_block_param(param, name);
+ const struct block_param *bp = config_get_block_param(param, name);
long value;
char *endptr;
@@ -624,7 +651,7 @@ bool
config_get_block_bool(const struct config_param *param, const char *name,
bool default_value)
{
- struct block_param *bp = config_get_block_param(param, name);
+ const struct block_param *bp = config_get_block_param(param, name);
bool success, value;
if (bp == NULL)
diff --git a/src/conf.h b/src/conf.h
index 8a0678312..815c739b1 100644
--- a/src/conf.h
+++ b/src/conf.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -72,6 +72,9 @@
#define CONF_PLAYLIST_PLUGIN "playlist_plugin"
#define CONF_AUTO_UPDATE "auto_update"
#define CONF_AUTO_UPDATE_DEPTH "auto_update_depth"
+#define CONF_DESPOTIFY_USER "despotify_user"
+#define CONF_DESPOTIFY_PASSWORD "despotify_password"
+#define CONF_DESPOTIFY_HIGH_BITRATE "despotify_high_bitrate"
#define DEFAULT_PLAYLIST_MAX_LENGTH (1024*16)
#define DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS false
@@ -108,6 +111,7 @@ struct config_param {
* A GQuark for GError instances, resulting from malformed
* configuration.
*/
+G_GNUC_CONST
static inline GQuark
config_quark(void)
{
@@ -129,11 +133,11 @@ config_read_file(const char *file, GError **error_r);
/* don't free the returned value
set _last_ to NULL to get first entry */
G_GNUC_PURE
-struct config_param *
+const struct config_param *
config_get_next_param(const char *name, const struct config_param *last);
G_GNUC_PURE
-static inline struct config_param *
+static inline const struct config_param *
config_get_param(const char *name)
{
return config_get_next_param(name, NULL);
@@ -152,17 +156,15 @@ config_get_string(const char *name, const char *default_value);
/**
* Returns an optional configuration variable which contains an
- * absolute path. If there is a tilde prefix, it is expanded. Aborts
- * MPD if the path is not a valid absolute path.
+ * absolute path. If there is a tilde prefix, it is expanded.
+ * Returns NULL if the value is not present. If the path could not be
+ * parsed, returns NULL and sets the error.
+ *
+ * The return value must be freed with g_free().
*/
-/* We lie here really. This function is not pure as it has side
- effects -- it parse the value and creates new string freeing
- previous one. However, because this works the very same way each
- time (ie. from the outside it appears as if function had no side
- effects) we should be in the clear declaring it pure. */
-G_GNUC_PURE
-const char *
-config_get_path(const char *name);
+G_GNUC_MALLOC
+char *
+config_dup_path(const char *name, GError **error_r);
G_GNUC_PURE
unsigned
@@ -173,7 +175,7 @@ unsigned
config_get_positive(const char *name, unsigned default_value);
G_GNUC_PURE
-struct block_param *
+const struct block_param *
config_get_block_param(const struct config_param *param, const char *name);
G_GNUC_PURE
@@ -184,6 +186,7 @@ const char *
config_get_block_string(const struct config_param *param, const char *name,
const char *default_value);
+G_GNUC_MALLOC
static inline char *
config_dup_block_string(const struct config_param *param, const char *name,
const char *default_value)
@@ -191,6 +194,15 @@ config_dup_block_string(const struct config_param *param, const char *name,
return g_strdup(config_get_block_string(param, name, default_value));
}
+/**
+ * Same as config_dup_path(), but looks up the setting in the
+ * specified block.
+ */
+G_GNUC_MALLOC
+char *
+config_dup_block_path(const struct config_param *param, const char *name,
+ GError **error_r);
+
G_GNUC_PURE
unsigned
config_get_block_unsigned(const struct config_param *param, const char *name,
@@ -201,11 +213,15 @@ bool
config_get_block_bool(const struct config_param *param, const char *name,
bool default_value);
+G_GNUC_MALLOC
struct config_param *
config_new_param(const char *value, int line);
-bool
+void
+config_param_free(struct config_param *param);
+
+void
config_add_block_param(struct config_param * param, const char *name,
- const char *value, int line, GError **error_r);
+ const char *value, int line);
#endif
diff --git a/src/crossfade.c b/src/crossfade.c
index cdfd82879..8bb2cde6b 100644
--- a/src/crossfade.c
+++ b/src/crossfade.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/crossfade.h b/src/crossfade.h
index 096a62020..d581dbfe0 100644
--- a/src/crossfade.h
+++ b/src/crossfade.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/cue/cue_tag.c b/src/cue/cue_tag.c
index ba1172559..6ee38bbd0 100644
--- a/src/cue/cue_tag.c
+++ b/src/cue/cue_tag.c
@@ -178,6 +178,15 @@ cue_tag(struct Cd *cd, unsigned tnum)
if (tag == NULL)
return NULL;
+ /* Create a tag number */
+
+ tag_clear_items_by_type(tag, TAG_TRACK);
+
+ char convert_uinttostring[8];
+ snprintf(convert_uinttostring, sizeof(convert_uinttostring),
+ "%02d/%02d", tnum, cd_get_ntrack(cd));
+ tag_add_item(tag, TAG_TRACK, convert_uinttostring);
+
tag->time = track_get_length(track)
- track_get_index(track, 1)
+ track_get_zero_pre(track);
diff --git a/src/daemon.c b/src/daemon.c
index 852541375..8bca9095a 100644
--- a/src/daemon.c
+++ b/src/daemon.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -80,7 +80,7 @@ daemonize_kill(void)
ret = kill(pid, SIGTERM);
if (ret < 0)
- MPD_ERROR("unable to kill proccess %i: %s",
+ MPD_ERROR("unable to kill process %i: %s",
pid, g_strerror(errno));
exit(EXIT_SUCCESS);
diff --git a/src/daemon.h b/src/daemon.h
index 71039543c..c43a74cfd 100644
--- a/src/daemon.h
+++ b/src/daemon.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/database.c b/src/database.c
index 9f29f95e1..9c4d90a2b 100644
--- a/src/database.c
+++ b/src/database.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,14 +19,16 @@
#include "config.h"
#include "database.h"
+#include "db_error.h"
+#include "db_save.h"
+#include "db_selection.h"
+#include "db_visitor.h"
+#include "db_plugin.h"
+#include "db/simple_db_plugin.h"
#include "directory.h"
-#include "directory_save.h"
-#include "song.h"
-#include "path.h"
#include "stats.h"
-#include "text_file.h"
-#include "tag.h"
-#include "tag_internal.h"
+#include "conf.h"
+#include "glib_compat.h"
#include <glib.h>
@@ -35,81 +37,58 @@
#include <unistd.h>
#include <assert.h>
#include <string.h>
-#include <stdlib.h>
#include <errno.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "database"
-#define DIRECTORY_INFO_BEGIN "info_begin"
-#define DIRECTORY_INFO_END "info_end"
-#define DB_FORMAT_PREFIX "format: "
-#define DIRECTORY_MPD_VERSION "mpd_version: "
-#define DIRECTORY_FS_CHARSET "fs_charset: "
-#define DB_TAG_PREFIX "tag: "
+static struct db *db;
+static bool db_is_open;
-enum {
- DB_FORMAT = 1,
-};
-
-static char *database_path;
-
-static struct directory *music_root;
-
-static time_t database_mtime;
-
-/**
- * The quark used for GError.domain.
- */
-static inline GQuark
-db_quark(void)
+bool
+db_init(const struct config_param *path, GError **error_r)
{
- return g_quark_from_static_string("database");
-}
+ assert(db == NULL);
+ assert(!db_is_open);
-void
-db_init(const char *path)
-{
- database_path = g_strdup(path);
+ if (path == NULL)
+ return true;
- if (path != NULL)
- music_root = directory_new("", NULL);
-}
+ struct config_param *param = config_new_param("database", path->line);
+ config_add_block_param(param, "path", path->value, path->line);
-void
-db_finish(void)
-{
- assert((database_path == NULL) == (music_root == NULL));
+ db = db_plugin_new(&simple_db_plugin, param, error_r);
- if (music_root != NULL)
- directory_free(music_root);
+ config_param_free(param);
- g_free(database_path);
+ return db != NULL;
}
void
-db_clear(void)
+db_finish(void)
{
- assert(music_root != NULL);
+ if (db_is_open)
+ db_plugin_close(db);
- directory_free(music_root);
- music_root = directory_new("", NULL);
+ if (db != NULL)
+ db_plugin_free(db);
}
struct directory *
db_get_root(void)
{
- assert(music_root != NULL);
+ assert(db != NULL);
- return music_root;
+ return simple_db_get_root(db);
}
struct directory *
db_get_directory(const char *name)
{
- if (music_root == NULL)
+ if (db == NULL)
return NULL;
+ struct directory *music_root = db_get_root();
if (name == NULL)
return music_root;
@@ -123,281 +102,67 @@ db_get_song(const char *file)
g_debug("get song: %s", file);
- if (music_root == NULL)
+ if (db == NULL)
return NULL;
- return directory_lookup_song(music_root, file);
+ return db_plugin_get_song(db, file, NULL);
}
-int
-db_walk(const char *name,
- int (*forEachSong)(struct song *, void *),
- int (*forEachDir)(struct directory *, void *), void *data)
+bool
+db_visit(const struct db_selection *selection,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r)
{
- struct directory *directory;
-
- if (music_root == NULL)
- return -1;
-
- if ((directory = db_get_directory(name)) == NULL) {
- struct song *song;
- if ((song = db_get_song(name)) && forEachSong) {
- return forEachSong(song, data);
- }
- return -1;
+ if (db == NULL) {
+ g_set_error_literal(error_r, db_quark(), DB_DISABLED,
+ "No database");
+ return false;
}
- return directory_walk(directory, forEachSong, forEachDir, data);
+ return db_plugin_visit(db, selection, visitor, ctx, error_r);
}
bool
-db_check(void)
+db_walk(const char *uri,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r)
{
- struct stat st;
-
- assert(database_path != NULL);
-
- /* Check if the file exists */
- if (access(database_path, F_OK)) {
- /* If the file doesn't exist, we can't check if we can write
- * it, so we are going to try to get the directory path, and
- * see if we can write a file in that */
- char *dirPath = g_path_get_dirname(database_path);
-
- /* Check that the parent part of the path is a directory */
- if (stat(dirPath, &st) < 0) {
- g_free(dirPath);
- g_warning("Couldn't stat parent directory of db file "
- "\"%s\": %s", database_path, strerror(errno));
- return false;
- }
-
- if (!S_ISDIR(st.st_mode)) {
- g_free(dirPath);
- g_warning("Couldn't create db file \"%s\" because the "
- "parent path is not a directory", database_path);
- return false;
- }
-
- /* Check if we can write to the directory */
- if (access(dirPath, X_OK | W_OK)) {
- g_warning("Can't create db file in \"%s\": %s",
- dirPath, strerror(errno));
- g_free(dirPath);
- return false;
- }
-
- g_free(dirPath);
-
- return true;
- }
-
- /* Path exists, now check if it's a regular file */
- if (stat(database_path, &st) < 0) {
- g_warning("Couldn't stat db file \"%s\": %s",
- database_path, strerror(errno));
- return false;
- }
+ struct db_selection selection;
+ db_selection_init(&selection, uri, true);
- if (!S_ISREG(st.st_mode)) {
- g_warning("db file \"%s\" is not a regular file", database_path);
- return false;
- }
-
- /* And check that we can write to it */
- if (access(database_path, R_OK | W_OK)) {
- g_warning("Can't open db file \"%s\" for reading/writing: %s",
- database_path, strerror(errno));
- return false;
- }
-
- return true;
+ return db_visit(&selection, visitor, ctx, error_r);
}
bool
-db_save(void)
+db_save(GError **error_r)
{
- FILE *fp;
- struct stat st;
-
- assert(database_path != NULL);
- assert(music_root != NULL);
+ assert(db != NULL);
+ assert(db_is_open);
- g_debug("removing empty directories from DB");
- directory_prune_empty(music_root);
-
- g_debug("sorting DB");
-
- directory_sort(music_root);
-
- g_debug("writing DB");
-
- fp = fopen(database_path, "w");
- if (!fp) {
- g_warning("unable to write to db file \"%s\": %s",
- database_path, strerror(errno));
- return false;
- }
-
- fprintf(fp, "%s\n", DIRECTORY_INFO_BEGIN);
- fprintf(fp, DB_FORMAT_PREFIX "%u\n", DB_FORMAT);
- fprintf(fp, "%s%s\n", DIRECTORY_MPD_VERSION, VERSION);
- fprintf(fp, "%s%s\n", DIRECTORY_FS_CHARSET, path_get_fs_charset());
-
- for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
- if (!ignore_tag_items[i])
- fprintf(fp, DB_TAG_PREFIX "%s\n", tag_item_names[i]);
-
- fprintf(fp, "%s\n", DIRECTORY_INFO_END);
-
- directory_save(fp, music_root);
-
- if (ferror(fp)) {
- g_warning("Failed to write to database file: %s",
- strerror(errno));
- fclose(fp);
- return false;
- }
-
- fclose(fp);
-
- if (stat(database_path, &st) == 0)
- database_mtime = st.st_mtime;
-
- return true;
+ return simple_db_save(db, error_r);
}
bool
db_load(GError **error)
{
- FILE *fp = NULL;
- struct stat st;
- GString *buffer = g_string_sized_new(1024);
- char *line;
- int format = 0;
- bool found_charset = false, found_version = false;
- bool success;
- bool tags[TAG_NUM_OF_ITEM_TYPES];
-
- assert(database_path != NULL);
- assert(music_root != NULL);
-
- fp = fopen(database_path, "r");
- if (fp == NULL) {
- g_set_error(error, db_quark(), errno,
- "Failed to open database file \"%s\": %s",
- database_path, strerror(errno));
- g_string_free(buffer, true);
- return false;
- }
-
- /* get initial info */
- line = read_text_line(fp, buffer);
- if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) {
- fclose(fp);
- g_set_error(error, db_quark(), 0, "Database corrupted");
- g_string_free(buffer, true);
- return false;
- }
-
- memset(tags, false, sizeof(tags));
-
- while ((line = read_text_line(fp, buffer)) != NULL &&
- strcmp(line, DIRECTORY_INFO_END) != 0) {
- if (g_str_has_prefix(line, DB_FORMAT_PREFIX)) {
- format = atoi(line + sizeof(DB_FORMAT_PREFIX) - 1);
- } else if (g_str_has_prefix(line, DIRECTORY_MPD_VERSION)) {
- if (found_version) {
- fclose(fp);
- g_set_error(error, db_quark(), 0,
- "Duplicate version line");
- g_string_free(buffer, true);
- return false;
- }
-
- found_version = true;
- } else if (g_str_has_prefix(line, DIRECTORY_FS_CHARSET)) {
- const char *new_charset, *old_charset;
-
- if (found_charset) {
- fclose(fp);
- g_set_error(error, db_quark(), 0,
- "Duplicate charset line");
- g_string_free(buffer, true);
- return false;
- }
-
- found_charset = true;
-
- new_charset = line + sizeof(DIRECTORY_FS_CHARSET) - 1;
- old_charset = path_get_fs_charset();
- if (old_charset != NULL
- && strcmp(new_charset, old_charset)) {
- fclose(fp);
- g_set_error(error, db_quark(), 0,
- "Existing database has charset "
- "\"%s\" instead of \"%s\"; "
- "discarding database file",
- new_charset, old_charset);
- g_string_free(buffer, true);
- return false;
- }
- } else if (g_str_has_prefix(line, DB_TAG_PREFIX)) {
- const char *name = line + sizeof(DB_TAG_PREFIX) - 1;
- enum tag_type tag = tag_name_parse(name);
- if (tag == TAG_NUM_OF_ITEM_TYPES) {
- g_set_error(error, db_quark(), 0,
- "Unrecognized tag '%s', "
- "discarding database file",
- name);
- return false;
- }
-
- tags[tag] = true;
- } else {
- fclose(fp);
- g_set_error(error, db_quark(), 0,
- "Malformed line: %s", line);
- g_string_free(buffer, true);
- return false;
- }
- }
+ assert(db != NULL);
+ assert(!db_is_open);
- if (format != DB_FORMAT) {
- g_set_error(error, db_quark(), 0,
- "Database format mismatch, "
- "discarding database file");
+ if (!db_plugin_open(db, error))
return false;
- }
-
- for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
- if (!ignore_tag_items[i] && !tags[i]) {
- g_set_error(error, db_quark(), 0,
- "Tag list mismatch, "
- "discarding database file");
- return false;
- }
- }
-
- g_debug("reading DB");
- success = directory_load(fp, music_root, buffer, error);
- g_string_free(buffer, true);
- fclose(fp);
-
- if (!success)
- return false;
+ db_is_open = true;
stats_update();
- if (stat(database_path, &st) == 0)
- database_mtime = st.st_mtime;
-
return true;
}
time_t
db_get_mtime(void)
{
- return database_mtime;
+ assert(db != NULL);
+ assert(db_is_open);
+
+ return simple_db_get_mtime(db);
}
diff --git a/src/database.h b/src/database.h
index 67149b20b..33f503652 100644
--- a/src/database.h
+++ b/src/database.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,52 +20,61 @@
#ifndef MPD_DATABASE_H
#define MPD_DATABASE_H
+#include "gcc.h"
+
#include <glib.h>
#include <sys/time.h>
#include <stdbool.h>
+struct config_param;
struct directory;
+struct db_selection;
+struct db_visitor;
/**
* Initialize the database library.
*
* @param path the absolute path of the database file
*/
-void
-db_init(const char *path);
+bool
+db_init(const struct config_param *path, GError **error_r);
void
db_finish(void);
/**
- * Clear the database.
- */
-void
-db_clear(void);
-
-/**
* Returns the root directory object. Returns NULL if there is no
* configured music directory.
*/
struct directory *
db_get_root(void);
+gcc_nonnull(1)
struct directory *
db_get_directory(const char *name);
+gcc_nonnull(1)
struct song *
db_get_song(const char *file);
-int db_walk(const char *name,
- int (*forEachSong)(struct song *, void *),
- int (*forEachDir)(struct directory *, void *), void *data);
+gcc_nonnull(1,2)
+bool
+db_visit(const struct db_selection *selection,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r);
+
+gcc_nonnull(1,2)
+bool
+db_walk(const char *uri,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r);
bool
-db_check(void);
+db_check(GError **error_r);
bool
-db_save(void);
+db_save(GError **error_r);
bool
db_load(GError **error);
diff --git a/src/db/simple_db_plugin.c b/src/db/simple_db_plugin.c
new file mode 100644
index 000000000..e359b5e65
--- /dev/null
+++ b/src/db/simple_db_plugin.c
@@ -0,0 +1,345 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "simple_db_plugin.h"
+#include "db_internal.h"
+#include "db_error.h"
+#include "db_selection.h"
+#include "db_visitor.h"
+#include "db_save.h"
+#include "conf.h"
+#include "glib_compat.h"
+#include "directory.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+struct simple_db {
+ struct db base;
+
+ char *path;
+
+ struct directory *root;
+
+ time_t mtime;
+};
+
+G_GNUC_CONST
+static inline GQuark
+simple_db_quark(void)
+{
+ return g_quark_from_static_string("simple_db");
+}
+
+G_GNUC_PURE
+static const struct directory *
+simple_db_lookup_directory(const struct simple_db *db, const char *uri)
+{
+ assert(db != NULL);
+ assert(db->root != NULL);
+ assert(uri != NULL);
+
+ return directory_lookup_directory(db->root, uri);
+}
+
+static struct db *
+simple_db_init(const struct config_param *param, GError **error_r)
+{
+ struct simple_db *db = g_malloc(sizeof(*db));
+ db_base_init(&db->base, &simple_db_plugin);
+
+ GError *error = NULL;
+ db->path = config_dup_block_path(param, "path", error_r);
+ if (db->path == NULL) {
+ g_free(db);
+ if (error != NULL)
+ g_propagate_error(error_r, error);
+ else
+ g_set_error(error_r, simple_db_quark(), 0,
+ "No \"path\" parameter specified");
+ return NULL;
+ }
+
+ return &db->base;
+}
+
+static void
+simple_db_finish(struct db *_db)
+{
+ struct simple_db *db = (struct simple_db *)_db;
+
+ g_free(db->path);
+ g_free(db);
+}
+
+static bool
+simple_db_check(struct simple_db *db, GError **error_r)
+{
+ assert(db != NULL);
+ assert(db->path != NULL);
+
+ /* Check if the file exists */
+ if (access(db->path, F_OK)) {
+ /* If the file doesn't exist, we can't check if we can write
+ * it, so we are going to try to get the directory path, and
+ * see if we can write a file in that */
+ char *dirPath = g_path_get_dirname(db->path);
+
+ /* Check that the parent part of the path is a directory */
+ struct stat st;
+ if (stat(dirPath, &st) < 0) {
+ g_free(dirPath);
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Couldn't stat parent directory of db file "
+ "\"%s\": %s",
+ db->path, g_strerror(errno));
+ return false;
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ g_free(dirPath);
+ g_set_error(error_r, simple_db_quark(), 0,
+ "Couldn't create db file \"%s\" because the "
+ "parent path is not a directory",
+ db->path);
+ return false;
+ }
+
+ /* Check if we can write to the directory */
+ if (access(dirPath, X_OK | W_OK)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Can't create db file in \"%s\": %s",
+ dirPath, g_strerror(errno));
+ g_free(dirPath);
+ return false;
+ }
+
+ g_free(dirPath);
+
+ return true;
+ }
+
+ /* Path exists, now check if it's a regular file */
+ struct stat st;
+ if (stat(db->path, &st) < 0) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Couldn't stat db file \"%s\": %s",
+ db->path, g_strerror(errno));
+ return false;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ g_set_error(error_r, simple_db_quark(), 0,
+ "db file \"%s\" is not a regular file",
+ db->path);
+ return false;
+ }
+
+ /* And check that we can write to it */
+ if (access(db->path, R_OK | W_OK)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Can't open db file \"%s\" for reading/writing: %s",
+ db->path, g_strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+simple_db_load(struct simple_db *db, GError **error_r)
+{
+ assert(db != NULL);
+ assert(db->path != NULL);
+ assert(db->root != NULL);
+
+ FILE *fp = fopen(db->path, "r");
+ if (fp == NULL) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Failed to open database file \"%s\": %s",
+ db->path, g_strerror(errno));
+ return false;
+ }
+
+ if (!db_load_internal(fp, db->root, error_r)) {
+ fclose(fp);
+ return false;
+ }
+
+ fclose(fp);
+
+ struct stat st;
+ if (stat(db->path, &st) == 0)
+ db->mtime = st.st_mtime;
+
+ return true;
+}
+
+static bool
+simple_db_open(struct db *_db, G_GNUC_UNUSED GError **error_r)
+{
+ struct simple_db *db = (struct simple_db *)_db;
+
+ db->root = directory_new("", NULL);
+ db->mtime = 0;
+
+ GError *error = NULL;
+ if (!simple_db_load(db, &error)) {
+ directory_free(db->root);
+
+ g_warning("Failed to load database: %s", error->message);
+ g_error_free(error);
+
+ if (!simple_db_check(db, error_r))
+ return false;
+
+ db->root = directory_new("", NULL);
+ }
+
+ return true;
+}
+
+static void
+simple_db_close(struct db *_db)
+{
+ struct simple_db *db = (struct simple_db *)_db;
+
+ assert(db->root != NULL);
+
+ directory_free(db->root);
+}
+
+static struct song *
+simple_db_get_song(struct db *_db, const char *uri, GError **error_r)
+{
+ struct simple_db *db = (struct simple_db *)_db;
+
+ assert(db->root != NULL);
+
+ struct song *song = directory_lookup_song(db->root, uri);
+ if (song == NULL)
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such song: %s", uri);
+
+ return song;
+}
+
+static bool
+simple_db_visit(struct db *_db, const struct db_selection *selection,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r)
+{
+ const struct simple_db *db = (const struct simple_db *)_db;
+ const struct directory *directory =
+ simple_db_lookup_directory(db, selection->uri);
+ if (directory == NULL) {
+ struct song *song;
+ if (visitor->song != NULL &&
+ (song = simple_db_get_song(_db, selection->uri, NULL)) != NULL)
+ return visitor->song(song, ctx, error_r);
+
+ g_set_error(error_r, db_quark(), DB_NOT_FOUND,
+ "No such directory");
+ return false;
+ }
+
+ if (selection->recursive && visitor->directory != NULL &&
+ !visitor->directory(directory, ctx, error_r))
+ return false;
+
+ return directory_walk(directory, selection->recursive,
+ visitor, ctx, error_r);
+}
+
+const struct db_plugin simple_db_plugin = {
+ .name = "simple",
+ .init = simple_db_init,
+ .finish = simple_db_finish,
+ .open = simple_db_open,
+ .close = simple_db_close,
+ .get_song = simple_db_get_song,
+ .visit = simple_db_visit,
+};
+
+struct directory *
+simple_db_get_root(struct db *_db)
+{
+ struct simple_db *db = (struct simple_db *)_db;
+
+ assert(db != NULL);
+ assert(db->root != NULL);
+
+ return db->root;
+}
+
+bool
+simple_db_save(struct db *_db, GError **error_r)
+{
+ struct simple_db *db = (struct simple_db *)_db;
+ struct directory *music_root = db->root;
+
+ g_debug("removing empty directories from DB");
+ directory_prune_empty(music_root);
+
+ g_debug("sorting DB");
+
+ directory_sort(music_root);
+
+ g_debug("writing DB");
+
+ FILE *fp = fopen(db->path, "w");
+ if (!fp) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "unable to write to db file \"%s\": %s",
+ db->path, g_strerror(errno));
+ return false;
+ }
+
+ db_save_internal(fp, music_root);
+
+ if (ferror(fp)) {
+ g_set_error(error_r, simple_db_quark(), errno,
+ "Failed to write to database file: %s",
+ g_strerror(errno));
+ fclose(fp);
+ return false;
+ }
+
+ fclose(fp);
+
+ struct stat st;
+ if (stat(db->path, &st) == 0)
+ db->mtime = st.st_mtime;
+
+ return true;
+}
+
+time_t
+simple_db_get_mtime(const struct db *_db)
+{
+ const struct simple_db *db = (const struct simple_db *)_db;
+
+ assert(db != NULL);
+ assert(db->root != NULL);
+
+ return db->mtime;
+}
diff --git a/src/db/simple_db_plugin.h b/src/db/simple_db_plugin.h
new file mode 100644
index 000000000..511505846
--- /dev/null
+++ b/src/db/simple_db_plugin.h
@@ -0,0 +1,42 @@
+/*
+ * 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_SIMPLE_DB_PLUGIN_H
+#define MPD_SIMPLE_DB_PLUGIN_H
+
+#include <glib.h>
+#include <stdbool.h>
+#include <time.h>
+
+extern const struct db_plugin simple_db_plugin;
+
+struct db;
+
+G_GNUC_PURE
+struct directory *
+simple_db_get_root(struct db *db);
+
+bool
+simple_db_save(struct db *db, GError **error_r);
+
+G_GNUC_PURE
+time_t
+simple_db_get_mtime(const struct db *db);
+
+#endif
diff --git a/src/dbUtils.c b/src/dbUtils.c
index f950d42cc..827d0a0c1 100644
--- a/src/dbUtils.c
+++ b/src/dbUtils.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,311 +20,105 @@
#include "config.h"
#include "dbUtils.h"
#include "locate.h"
-#include "directory.h"
#include "database.h"
-#include "client.h"
+#include "db_visitor.h"
#include "playlist.h"
-#include "song.h"
-#include "song_print.h"
-#include "tag.h"
-#include "strset.h"
#include "stored_playlist.h"
#include <glib.h>
-#include <stdlib.h>
-
-typedef struct _ListCommandItem {
- int8_t tagType;
- const struct locate_item_list *criteria;
-} ListCommandItem;
-
-typedef struct _SearchStats {
- const struct locate_item_list *criteria;
- int numberOfSongs;
- unsigned long playTime;
-} SearchStats;
-
-static int
-printDirectoryInDirectory(struct directory *directory, void *data)
-{
- struct client *client = data;
-
- if (!directory_is_root(directory))
- client_printf(client, "directory: %s\n", directory_get_path(directory));
-
- return 0;
-}
-
-static int
-printSongInDirectory(struct song *song, G_GNUC_UNUSED void *data)
-{
- struct client *client = data;
- song_print_uri(client, song);
- return 0;
-}
-
-struct search_data {
- struct client *client;
- const struct locate_item_list *criteria;
-};
-
-static int
-searchInDirectory(struct song *song, void *_data)
-{
- struct search_data *data = _data;
-
- if (locate_song_search(song, data->criteria))
- song_print_info(data->client, song);
-
- return 0;
-}
-
-int
-searchForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria)
-{
- int ret;
- struct locate_item_list *new_list
- = locate_item_list_casefold(criteria);
- struct search_data data;
-
- data.client = client;
- data.criteria = new_list;
-
- ret = db_walk(name, searchInDirectory, NULL, &data);
-
- locate_item_list_free(new_list);
-
- return ret;
-}
-
-static int
-findInDirectory(struct song *song, void *_data)
+static bool
+add_to_queue_song(struct song *song, void *ctx, GError **error_r)
{
- struct search_data *data = _data;
-
- if (locate_song_match(song, data->criteria))
- song_print_info(data->client, song);
+ struct player_control *pc = ctx;
- return 0;
-}
-
-int
-findSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria)
-{
- struct search_data data;
-
- data.client = client;
- data.criteria = criteria;
-
- return db_walk(name, findInDirectory, NULL, &data);
-}
-
-static void printSearchStats(struct client *client, SearchStats *stats)
-{
- client_printf(client, "songs: %i\n", stats->numberOfSongs);
- client_printf(client, "playtime: %li\n", stats->playTime);
-}
-
-static int
-searchStatsInDirectory(struct song *song, void *data)
-{
- SearchStats *stats = data;
-
- if (locate_song_match(song, stats->criteria)) {
- stats->numberOfSongs++;
- stats->playTime += song_get_duration(song);
+ enum playlist_result result =
+ playlist_append_song(&g_playlist, pc, song, NULL);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ g_set_error(error_r, playlist_quark(), result,
+ "Playlist error");
+ return false;
}
- return 0;
-}
-
-int
-searchStatsForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria)
-{
- SearchStats stats;
- int ret;
-
- stats.criteria = criteria;
- stats.numberOfSongs = 0;
- stats.playTime = 0;
-
- ret = db_walk(name, searchStatsInDirectory, NULL, &stats);
- if (ret == 0)
- printSearchStats(client, &stats);
-
- return ret;
+ return true;
}
-int printAllIn(struct client *client, const char *name)
-{
- return db_walk(name, printSongInDirectory,
- printDirectoryInDirectory, client);
-}
+static const struct db_visitor add_to_queue_visitor = {
+ .song = add_to_queue_song,
+};
-static int
-directoryAddSongToPlaylist(struct song *song, G_GNUC_UNUSED void *data)
+bool
+addAllIn(struct player_control *pc, const char *uri, GError **error_r)
{
- return playlist_append_song(&g_playlist, song, NULL);
+ return db_walk(uri, &add_to_queue_visitor, pc, error_r);
}
struct add_data {
const char *path;
};
-static int
-directoryAddSongToStoredPlaylist(struct song *song, void *_data)
+static bool
+add_to_spl_song(struct song *song, void *ctx, GError **error_r)
{
- struct add_data *data = _data;
+ struct add_data *data = ctx;
- if (spl_append_song(data->path, song) != 0)
- return -1;
- return 0;
-}
+ if (!spl_append_song(data->path, song, error_r))
+ return false;
-int addAllIn(const char *name)
-{
- return db_walk(name, directoryAddSongToPlaylist, NULL, NULL);
+ return true;
}
-int addAllInToStoredPlaylist(const char *name, const char *utf8file)
+static const struct db_visitor add_to_spl_visitor = {
+ .song = add_to_spl_song,
+};
+
+bool
+addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
+ GError **error_r)
{
struct add_data data = {
- .path = utf8file,
+ .path = path_utf8,
};
- return db_walk(name, directoryAddSongToStoredPlaylist, NULL, &data);
+ return db_walk(uri_utf8, &add_to_spl_visitor, &data, error_r);
}
-static int
-findAddInDirectory(struct song *song, void *_data)
-{
- struct search_data *data = _data;
-
- if (locate_song_match(song, data->criteria))
- return directoryAddSongToPlaylist(song, data);
-
- return 0;
-}
-
-int findAddIn(struct client *client, const char *name,
- const struct locate_item_list *criteria)
-{
- struct search_data data;
-
- data.client = client;
- data.criteria = criteria;
-
- return db_walk(name, findAddInDirectory, NULL, &data);
-}
-
-static int
-directoryPrintSongInfo(struct song *song, void *data)
-{
- struct client *client = data;
- song_print_info(client, song);
- return 0;
-}
-
-int printInfoForAllIn(struct client *client, const char *name)
-{
- return db_walk(name, directoryPrintSongInfo,
- printDirectoryInDirectory, client);
-}
-
-static ListCommandItem *
-newListCommandItem(int tagType, const struct locate_item_list *criteria)
-{
- ListCommandItem *item = g_new(ListCommandItem, 1);
-
- item->tagType = tagType;
- item->criteria = criteria;
-
- return item;
-}
-
-static void freeListCommandItem(ListCommandItem * item)
-{
- g_free(item);
-}
+struct find_add_data {
+ struct player_control *pc;
+ const struct locate_item_list *criteria;
+};
-static void
-visitTag(struct client *client, struct strset *set,
- struct song *song, enum tag_type tagType)
+static bool
+find_add_song(struct song *song, void *ctx, GError **error_r)
{
- struct tag *tag = song->tag;
- bool found = false;
+ struct find_add_data *data = ctx;
- if (tagType == LOCATE_TAG_FILE_TYPE) {
- song_print_uri(client, song);
- return;
- }
-
- if (!tag)
- return;
+ if (!locate_song_match(song, data->criteria))
+ return true;
- for (unsigned i = 0; i < tag->num_items; i++) {
- if (tag->items[i]->type == tagType) {
- strset_add(set, tag->items[i]->value);
- found = true;
- }
+ enum playlist_result result =
+ playlist_append_song(&g_playlist, data->pc,
+ song, NULL);
+ if (result != PLAYLIST_RESULT_SUCCESS) {
+ g_set_error(error_r, playlist_quark(), result,
+ "Playlist error");
+ return false;
}
- if (!found)
- strset_add(set, "");
+ return true;
}
-struct list_tags_data {
- struct client *client;
- ListCommandItem *item;
- struct strset *set;
+static const struct db_visitor find_add_visitor = {
+ .song = find_add_song,
};
-static int
-listUniqueTagsInDirectory(struct song *song, void *_data)
-{
- struct list_tags_data *data = _data;
- ListCommandItem *item = data->item;
-
- if (locate_song_match(song, item->criteria))
- visitTag(data->client, data->set, song, item->tagType);
-
- return 0;
-}
-
-int listAllUniqueTags(struct client *client, int type,
- const struct locate_item_list *criteria)
+bool
+findAddIn(struct player_control *pc, const char *name,
+ const struct locate_item_list *criteria, GError **error_r)
{
- int ret;
- ListCommandItem *item = newListCommandItem(type, criteria);
- struct list_tags_data data = {
- .client = client,
- .item = item,
- };
-
- if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- data.set = strset_new();
- }
-
- ret = db_walk(NULL, listUniqueTagsInDirectory, NULL, &data);
-
- if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
- const char *value;
-
- strset_rewind(data.set);
-
- while ((value = strset_next(data.set)) != NULL)
- client_printf(client, "%s: %s\n",
- tag_item_names[type],
- value);
-
- strset_free(data.set);
- }
-
- freeListCommandItem(item);
+ struct find_add_data data;
+ data.pc = pc;
+ data.criteria = criteria;
- return ret;
+ return db_walk(name, &find_add_visitor, &data, error_r);
}
diff --git a/src/dbUtils.h b/src/dbUtils.h
index bba253154..40594652b 100644
--- a/src/dbUtils.h
+++ b/src/dbUtils.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,37 +20,26 @@
#ifndef MPD_DB_UTILS_H
#define MPD_DB_UTILS_H
-struct client;
-struct locate_item_list;
-
-int printAllIn(struct client *client, const char *name);
-
-int addAllIn(const char *name);
-
-int addAllInToStoredPlaylist(const char *name, const char *utf8file);
+#include "gcc.h"
-int printInfoForAllIn(struct client *client, const char *name);
+#include <glib.h>
+#include <stdbool.h>
-int
-searchForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria);
-
-int
-findSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria);
-
-int
-findAddIn(struct client *client, const char *name,
- const struct locate_item_list *criteria);
+struct locate_item_list;
+struct player_control;
-int
-searchStatsForSongsIn(struct client *client, const char *name,
- const struct locate_item_list *criteria);
+gcc_nonnull(1,2)
+bool
+addAllIn(struct player_control *pc, const char *uri, GError **error_r);
-unsigned long sumSongTimesIn(const char *name);
+gcc_nonnull(1,2)
+bool
+addAllInToStoredPlaylist(const char *uri_utf8, const char *path_utf8,
+ GError **error_r);
-int
-listAllUniqueTags(struct client *client, int type,
- const struct locate_item_list *criteria);
+gcc_nonnull(1,2,3)
+bool
+findAddIn(struct player_control *pc, const char *name,
+ const struct locate_item_list *criteria, GError **error_r);
#endif
diff --git a/src/db_error.h b/src/db_error.h
new file mode 100644
index 000000000..d3be582cf
--- /dev/null
+++ b/src/db_error.h
@@ -0,0 +1,45 @@
+/*
+ * 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_DB_ERROR_H
+#define MPD_DB_ERROR_H
+
+#include <glib.h>
+
+enum db_error {
+ /**
+ * The database is disabled, i.e. none is configured in this
+ * MPD instance.
+ */
+ DB_DISABLED,
+
+ DB_NOT_FOUND,
+};
+
+/**
+ * Quark for GError.domain; the code is an enum #db_error.
+ */
+G_GNUC_CONST
+static inline GQuark
+db_quark(void)
+{
+ return g_quark_from_static_string("db");
+}
+
+#endif
diff --git a/src/db_internal.h b/src/db_internal.h
new file mode 100644
index 000000000..a33351524
--- /dev/null
+++ b/src/db_internal.h
@@ -0,0 +1,35 @@
+/*
+ * 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_DB_INTERNAL_H
+#define MPD_DB_INTERNAL_H
+
+#include "db_plugin.h"
+
+#include <assert.h>
+
+static inline void
+db_base_init(struct db *db, const struct db_plugin *plugin)
+{
+ assert(plugin != NULL);
+
+ db->plugin = plugin;
+}
+
+#endif
diff --git a/src/db_plugin.h b/src/db_plugin.h
new file mode 100644
index 000000000..1c7e14ede
--- /dev/null
+++ b/src/db_plugin.h
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+/** \file
+ *
+ * This header declares the db_plugin class. It describes a
+ * plugin API for databases of song metadata.
+ */
+
+#ifndef MPD_DB_PLUGIN_H
+#define MPD_DB_PLUGIN_H
+
+#include <glib.h>
+#include <assert.h>
+#include <stdbool.h>
+
+struct config_param;
+struct db_selection;
+struct db_visitor;
+
+struct db {
+ const struct db_plugin *plugin;
+};
+
+struct db_plugin {
+ const char *name;
+
+ /**
+ * Allocates and configures a database.
+ */
+ struct db *(*init)(const struct config_param *param, GError **error_r);
+
+ /**
+ * Free instance data.
+ */
+ void (*finish)(struct db *db);
+
+ /**
+ * Open the database. Read it into memory if applicable.
+ */
+ bool (*open)(struct db *db, GError **error_r);
+
+ /**
+ * Close the database, free allocated memory.
+ */
+ void (*close)(struct db *db);
+
+ /**
+ * Look up a song (including tag data) in the database.
+ *
+ * @param the URI of the song within the music directory
+ * (UTF-8)
+ */
+ struct song *(*get_song)(struct db *db, const char *uri,
+ GError **error_r);
+
+ /**
+ * Visit the selected entities.
+ */
+ bool (*visit)(struct db *db, const struct db_selection *selection,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r);
+};
+
+G_GNUC_MALLOC
+static inline struct db *
+db_plugin_new(const struct db_plugin *plugin, const struct config_param *param,
+ GError **error_r)
+{
+ assert(plugin != NULL);
+ assert(plugin->init != NULL);
+ assert(plugin->finish != NULL);
+ assert(plugin->get_song != NULL);
+ assert(plugin->visit != NULL);
+ assert(error_r == NULL || *error_r == NULL);
+
+ struct db *db = plugin->init(param, error_r);
+ assert(db == NULL || db->plugin == plugin);
+ assert(db != NULL || error_r == NULL || *error_r != NULL);
+
+ return db;
+}
+
+static inline void
+db_plugin_free(struct db *db)
+{
+ assert(db != NULL);
+ assert(db->plugin != NULL);
+ assert(db->plugin->finish != NULL);
+
+ db->plugin->finish(db);
+}
+
+static inline bool
+db_plugin_open(struct db *db, GError **error_r)
+{
+ assert(db != NULL);
+ assert(db->plugin != NULL);
+
+ return db->plugin->open != NULL
+ ? db->plugin->open(db, error_r)
+ : true;
+}
+
+static inline void
+db_plugin_close(struct db *db)
+{
+ assert(db != NULL);
+ assert(db->plugin != NULL);
+
+ if (db->plugin->close != NULL)
+ db->plugin->close(db);
+}
+
+static inline struct song *
+db_plugin_get_song(struct db *db, const char *uri, GError **error_r)
+{
+ assert(db != NULL);
+ assert(db->plugin != NULL);
+ assert(db->plugin->get_song != NULL);
+ assert(uri != NULL);
+
+ return db->plugin->get_song(db, uri, error_r);
+}
+
+static inline bool
+db_plugin_visit(struct db *db, const struct db_selection *selection,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r)
+{
+ assert(db != NULL);
+ assert(db->plugin != NULL);
+ assert(selection != NULL);
+ assert(visitor != NULL);
+ assert(error_r == NULL || *error_r == NULL);
+
+ return db->plugin->visit(db, selection, visitor, ctx, error_r);
+}
+
+#endif
diff --git a/src/db_print.c b/src/db_print.c
new file mode 100644
index 000000000..f341ca4e8
--- /dev/null
+++ b/src/db_print.c
@@ -0,0 +1,360 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "db_print.h"
+#include "db_selection.h"
+#include "db_visitor.h"
+#include "locate.h"
+#include "directory.h"
+#include "database.h"
+#include "client.h"
+#include "song.h"
+#include "song_print.h"
+#include "tag.h"
+#include "strset.h"
+
+#include <glib.h>
+
+typedef struct _ListCommandItem {
+ int8_t tagType;
+ const struct locate_item_list *criteria;
+} ListCommandItem;
+
+typedef struct _SearchStats {
+ const struct locate_item_list *criteria;
+ int numberOfSongs;
+ unsigned long playTime;
+} SearchStats;
+
+static bool
+print_visitor_directory(const struct directory *directory, void *data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct client *client = data;
+
+ if (!directory_is_root(directory))
+ client_printf(client, "directory: %s\n", directory_get_path(directory));
+
+ return true;
+}
+
+static bool
+print_visitor_song(struct song *song, void *data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct client *client = data;
+ song_print_uri(client, song);
+ return true;
+}
+
+static bool
+print_visitor_song_info(struct song *song, void *data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct client *client = data;
+ song_print_info(client, song);
+ return true;
+}
+
+static bool
+print_visitor_playlist(const struct playlist_metadata *playlist, void *ctx,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct client *client = ctx;
+ client_printf(client, "playlist: %s\n", playlist->name);
+ return true;
+}
+
+static bool
+print_visitor_playlist_info(const struct playlist_metadata *playlist,
+ void *ctx, G_GNUC_UNUSED GError **error_r)
+{
+ struct client *client = ctx;
+ client_printf(client, "playlist: %s\n", playlist->name);
+
+#ifndef G_OS_WIN32
+ struct tm tm;
+#endif
+ char timestamp[32];
+ time_t t = playlist->mtime;
+ strftime(timestamp, sizeof(timestamp),
+#ifdef G_OS_WIN32
+ "%Y-%m-%dT%H:%M:%SZ",
+ gmtime(&t)
+#else
+ "%FT%TZ",
+ gmtime_r(&t, &tm)
+#endif
+ );
+ client_printf(client, "Last-Modified: %s\n", timestamp);
+
+ return true;
+}
+
+static const struct db_visitor print_visitor = {
+ .directory = print_visitor_directory,
+ .song = print_visitor_song,
+ .playlist = print_visitor_playlist,
+};
+
+static const struct db_visitor print_info_visitor = {
+ .directory = print_visitor_directory,
+ .song = print_visitor_song_info,
+ .playlist = print_visitor_playlist_info,
+};
+
+bool
+db_selection_print(struct client *client, const struct db_selection *selection,
+ bool full, GError **error_r)
+{
+ return db_visit(selection, full ? &print_info_visitor : &print_visitor,
+ client, error_r);
+}
+
+struct search_data {
+ struct client *client;
+ const struct locate_item_list *criteria;
+};
+
+static bool
+search_visitor_song(struct song *song, void *_data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct search_data *data = _data;
+
+ if (locate_song_search(song, data->criteria))
+ song_print_info(data->client, song);
+
+ return true;
+}
+
+static const struct db_visitor search_visitor = {
+ .song = search_visitor_song,
+};
+
+bool
+searchForSongsIn(struct client *client, const char *name,
+ const struct locate_item_list *criteria,
+ GError **error_r)
+{
+ struct locate_item_list *new_list
+ = locate_item_list_casefold(criteria);
+ struct search_data data;
+
+ data.client = client;
+ data.criteria = new_list;
+
+ bool success = db_walk(name, &search_visitor, &data, error_r);
+
+ locate_item_list_free(new_list);
+
+ return success;
+}
+
+static bool
+find_visitor_song(struct song *song, void *_data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct search_data *data = _data;
+
+ if (locate_song_match(song, data->criteria))
+ song_print_info(data->client, song);
+
+ return true;
+}
+
+static const struct db_visitor find_visitor = {
+ .song = find_visitor_song,
+};
+
+bool
+findSongsIn(struct client *client, const char *name,
+ const struct locate_item_list *criteria,
+ GError **error_r)
+{
+ struct search_data data;
+
+ data.client = client;
+ data.criteria = criteria;
+
+ return db_walk(name, &find_visitor, &data, error_r);
+}
+
+static void printSearchStats(struct client *client, SearchStats *stats)
+{
+ client_printf(client, "songs: %i\n", stats->numberOfSongs);
+ client_printf(client, "playtime: %li\n", stats->playTime);
+}
+
+static bool
+stats_visitor_song(struct song *song, void *data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ SearchStats *stats = data;
+
+ if (locate_song_match(song, stats->criteria)) {
+ stats->numberOfSongs++;
+ stats->playTime += song_get_duration(song);
+ }
+
+ return true;
+}
+
+static const struct db_visitor stats_visitor = {
+ .song = stats_visitor_song,
+};
+
+bool
+searchStatsForSongsIn(struct client *client, const char *name,
+ const struct locate_item_list *criteria,
+ GError **error_r)
+{
+ SearchStats stats;
+
+ stats.criteria = criteria;
+ stats.numberOfSongs = 0;
+ stats.playTime = 0;
+
+ if (!db_walk(name, &stats_visitor, &stats, error_r))
+ return false;
+
+ printSearchStats(client, &stats);
+ return true;
+}
+
+bool
+printAllIn(struct client *client, const char *uri_utf8, GError **error_r)
+{
+ struct db_selection selection;
+ db_selection_init(&selection, uri_utf8, true);
+ return db_selection_print(client, &selection, false, error_r);
+}
+
+bool
+printInfoForAllIn(struct client *client, const char *uri_utf8,
+ GError **error_r)
+{
+ struct db_selection selection;
+ db_selection_init(&selection, uri_utf8, true);
+ return db_selection_print(client, &selection, true, error_r);
+}
+
+static ListCommandItem *
+newListCommandItem(int tagType, const struct locate_item_list *criteria)
+{
+ ListCommandItem *item = g_new(ListCommandItem, 1);
+
+ item->tagType = tagType;
+ item->criteria = criteria;
+
+ return item;
+}
+
+static void freeListCommandItem(ListCommandItem * item)
+{
+ g_free(item);
+}
+
+static void
+visitTag(struct client *client, struct strset *set,
+ struct song *song, enum tag_type tagType)
+{
+ struct tag *tag = song->tag;
+ bool found = false;
+
+ if (tagType == LOCATE_TAG_FILE_TYPE) {
+ song_print_uri(client, song);
+ return;
+ }
+
+ if (!tag)
+ return;
+
+ for (unsigned i = 0; i < tag->num_items; i++) {
+ if (tag->items[i]->type == tagType) {
+ strset_add(set, tag->items[i]->value);
+ found = true;
+ }
+ }
+
+ if (!found)
+ strset_add(set, "");
+}
+
+struct list_tags_data {
+ struct client *client;
+ ListCommandItem *item;
+ struct strset *set;
+};
+
+static bool
+unique_tags_visitor_song(struct song *song, void *_data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct list_tags_data *data = _data;
+ ListCommandItem *item = data->item;
+
+ if (locate_song_match(song, item->criteria))
+ visitTag(data->client, data->set, song, item->tagType);
+
+ return true;
+}
+
+static const struct db_visitor unique_tags_visitor = {
+ .song = unique_tags_visitor_song,
+};
+
+bool
+listAllUniqueTags(struct client *client, int type,
+ const struct locate_item_list *criteria,
+ GError **error_r)
+{
+ ListCommandItem *item = newListCommandItem(type, criteria);
+ struct list_tags_data data = {
+ .client = client,
+ .item = item,
+ };
+
+ if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
+ data.set = strset_new();
+ }
+
+ if (!db_walk("", &unique_tags_visitor, &data, error_r)) {
+ freeListCommandItem(item);
+ return false;
+ }
+
+ if (type >= 0 && type <= TAG_NUM_OF_ITEM_TYPES) {
+ const char *value;
+
+ strset_rewind(data.set);
+
+ while ((value = strset_next(data.set)) != NULL)
+ client_printf(client, "%s: %s\n",
+ tag_item_names[type],
+ value);
+
+ strset_free(data.set);
+ }
+
+ freeListCommandItem(item);
+
+ return true;
+}
diff --git a/src/db_print.h b/src/db_print.h
new file mode 100644
index 000000000..1b957da18
--- /dev/null
+++ b/src/db_print.h
@@ -0,0 +1,71 @@
+/*
+ * 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_DB_PRINT_H
+#define MPD_DB_PRINT_H
+
+#include "gcc.h"
+
+#include <glib.h>
+#include <stdbool.h>
+
+struct client;
+struct locate_item_list;
+struct db_selection;
+struct db_visitor;
+
+gcc_nonnull(1,2)
+bool
+db_selection_print(struct client *client, const struct db_selection *selection,
+ bool full, GError **error_r);
+
+gcc_nonnull(1,2)
+bool
+printAllIn(struct client *client, const char *uri_utf8, GError **error_r);
+
+gcc_nonnull(1,2)
+bool
+printInfoForAllIn(struct client *client, const char *uri_utf8,
+ GError **error_r);
+
+gcc_nonnull(1,2,3)
+bool
+searchForSongsIn(struct client *client, const char *name,
+ const struct locate_item_list *criteria,
+ GError **error_r);
+
+gcc_nonnull(1,2,3)
+bool
+findSongsIn(struct client *client, const char *name,
+ const struct locate_item_list *criteria,
+ GError **error_r);
+
+gcc_nonnull(1,2,3)
+bool
+searchStatsForSongsIn(struct client *client, const char *name,
+ const struct locate_item_list *criteria,
+ GError **error_r);
+
+gcc_nonnull(1,3)
+bool
+listAllUniqueTags(struct client *client, int type,
+ const struct locate_item_list *criteria,
+ GError **error_r);
+
+#endif
diff --git a/src/db_save.c b/src/db_save.c
new file mode 100644
index 000000000..00967f4f2
--- /dev/null
+++ b/src/db_save.c
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "db_save.h"
+#include "directory.h"
+#include "directory_save.h"
+#include "song.h"
+#include "path.h"
+#include "text_file.h"
+#include "tag.h"
+#include "tag_internal.h"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <stdlib.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "database"
+
+#define DIRECTORY_INFO_BEGIN "info_begin"
+#define DIRECTORY_INFO_END "info_end"
+#define DB_FORMAT_PREFIX "format: "
+#define DIRECTORY_MPD_VERSION "mpd_version: "
+#define DIRECTORY_FS_CHARSET "fs_charset: "
+#define DB_TAG_PREFIX "tag: "
+
+enum {
+ DB_FORMAT = 1,
+};
+
+G_GNUC_CONST
+static GQuark
+db_quark(void)
+{
+ return g_quark_from_static_string("database");
+}
+
+void
+db_save_internal(FILE *fp, const struct directory *music_root)
+{
+ assert(music_root != NULL);
+
+ fprintf(fp, "%s\n", DIRECTORY_INFO_BEGIN);
+ fprintf(fp, DB_FORMAT_PREFIX "%u\n", DB_FORMAT);
+ fprintf(fp, "%s%s\n", DIRECTORY_MPD_VERSION, VERSION);
+ fprintf(fp, "%s%s\n", DIRECTORY_FS_CHARSET, path_get_fs_charset());
+
+ for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i)
+ if (!ignore_tag_items[i])
+ fprintf(fp, DB_TAG_PREFIX "%s\n", tag_item_names[i]);
+
+ fprintf(fp, "%s\n", DIRECTORY_INFO_END);
+
+ directory_save(fp, music_root);
+}
+
+bool
+db_load_internal(FILE *fp, struct directory *music_root, GError **error)
+{
+ GString *buffer = g_string_sized_new(1024);
+ char *line;
+ int format = 0;
+ bool found_charset = false, found_version = false;
+ bool success;
+ bool tags[TAG_NUM_OF_ITEM_TYPES];
+
+ assert(music_root != NULL);
+
+ /* get initial info */
+ line = read_text_line(fp, buffer);
+ if (line == NULL || strcmp(DIRECTORY_INFO_BEGIN, line) != 0) {
+ g_set_error(error, db_quark(), 0, "Database corrupted");
+ g_string_free(buffer, true);
+ return false;
+ }
+
+ memset(tags, false, sizeof(tags));
+
+ while ((line = read_text_line(fp, buffer)) != NULL &&
+ strcmp(line, DIRECTORY_INFO_END) != 0) {
+ if (g_str_has_prefix(line, DB_FORMAT_PREFIX)) {
+ format = atoi(line + sizeof(DB_FORMAT_PREFIX) - 1);
+ } else if (g_str_has_prefix(line, DIRECTORY_MPD_VERSION)) {
+ if (found_version) {
+ g_set_error(error, db_quark(), 0,
+ "Duplicate version line");
+ g_string_free(buffer, true);
+ return false;
+ }
+
+ found_version = true;
+ } else if (g_str_has_prefix(line, DIRECTORY_FS_CHARSET)) {
+ const char *new_charset, *old_charset;
+
+ if (found_charset) {
+ g_set_error(error, db_quark(), 0,
+ "Duplicate charset line");
+ g_string_free(buffer, true);
+ return false;
+ }
+
+ found_charset = true;
+
+ new_charset = line + sizeof(DIRECTORY_FS_CHARSET) - 1;
+ old_charset = path_get_fs_charset();
+ if (old_charset != NULL
+ && strcmp(new_charset, old_charset)) {
+ g_set_error(error, db_quark(), 0,
+ "Existing database has charset "
+ "\"%s\" instead of \"%s\"; "
+ "discarding database file",
+ new_charset, old_charset);
+ g_string_free(buffer, true);
+ return false;
+ }
+ } else if (g_str_has_prefix(line, DB_TAG_PREFIX)) {
+ const char *name = line + sizeof(DB_TAG_PREFIX) - 1;
+ enum tag_type tag = tag_name_parse(name);
+ if (tag == TAG_NUM_OF_ITEM_TYPES) {
+ g_set_error(error, db_quark(), 0,
+ "Unrecognized tag '%s', "
+ "discarding database file",
+ name);
+ return false;
+ }
+
+ tags[tag] = true;
+ } else {
+ g_set_error(error, db_quark(), 0,
+ "Malformed line: %s", line);
+ g_string_free(buffer, true);
+ return false;
+ }
+ }
+
+ if (format != DB_FORMAT) {
+ g_set_error(error, db_quark(), 0,
+ "Database format mismatch, "
+ "discarding database file");
+ return false;
+ }
+
+ for (unsigned i = 0; i < TAG_NUM_OF_ITEM_TYPES; ++i) {
+ if (!ignore_tag_items[i] && !tags[i]) {
+ g_set_error(error, db_quark(), 0,
+ "Tag list mismatch, "
+ "discarding database file");
+ return false;
+ }
+ }
+
+ g_debug("reading DB");
+
+ success = directory_load(fp, music_root, buffer, error);
+ g_string_free(buffer, true);
+
+ return success;
+}
diff --git a/src/db_save.h b/src/db_save.h
new file mode 100644
index 000000000..e760ec881
--- /dev/null
+++ b/src/db_save.h
@@ -0,0 +1,35 @@
+/*
+ * 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_DB_SAVE_H
+#define MPD_DB_SAVE_H
+
+#include <glib.h>
+#include <stdbool.h>
+#include <stdio.h>
+
+struct directory;
+
+void
+db_save_internal(FILE *file, const struct directory *root);
+
+bool
+db_load_internal(FILE *file, struct directory *root, GError **error);
+
+#endif
diff --git a/src/db_selection.h b/src/db_selection.h
new file mode 100644
index 000000000..2cebb4907
--- /dev/null
+++ b/src/db_selection.h
@@ -0,0 +1,56 @@
+/*
+ * 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_DB_SELECTION_H
+#define MPD_DB_SELECTION_H
+
+#include "gcc.h"
+
+#include <assert.h>
+
+struct directory;
+struct song;
+
+struct db_selection {
+ /**
+ * The base URI of the search (UTF-8). Must not begin or end
+ * with a slash. NULL or an empty string searches the whole
+ * database.
+ */
+ const char *uri;
+
+ /**
+ * Recursively search all sub directories?
+ */
+ bool recursive;
+};
+
+gcc_nonnull(1,2)
+static inline void
+db_selection_init(struct db_selection *selection,
+ const char *uri, bool recursive)
+{
+ assert(selection != NULL);
+ assert(uri != NULL);
+
+ selection->uri = uri;
+ selection->recursive = recursive;
+}
+
+#endif
diff --git a/src/db_visitor.h b/src/db_visitor.h
new file mode 100644
index 000000000..f68054ec2
--- /dev/null
+++ b/src/db_visitor.h
@@ -0,0 +1,52 @@
+/*
+ * 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_DB_VISITOR_H
+#define MPD_DB_VISITOR_H
+
+struct directory;
+struct song;
+struct playlist_metadata;
+
+struct db_visitor {
+ /**
+ * Visit a directory. Optional method.
+ *
+ * @return true to continue the operation, false on error (set error_r)
+ */
+ bool (*directory)(const struct directory *directory, void *ctx,
+ GError **error_r);
+
+ /**
+ * Visit a song. Optional method.
+ *
+ * @return true to continue the operation, false on error (set error_r)
+ */
+ bool (*song)(struct song *song, void *ctx, GError **error_r);
+
+ /**
+ * Visit a playlist. Optional method.
+ *
+ * @return true to continue the operation, false on error (set error_r)
+ */
+ bool (*playlist)(const struct playlist_metadata *playlist, void *ctx,
+ GError **error_r);
+};
+
+#endif
diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c
index 8dd22a253..fc42e5913 100644
--- a/src/decoder/_flac_common.c
+++ b/src/decoder/_flac_common.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/_flac_common.h b/src/decoder/_flac_common.h
index 5c59ee123..1f4cbb273 100644
--- a/src/decoder/_flac_common.h
+++ b/src/decoder/_flac_common.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/_ogg_common.c b/src/decoder/_ogg_common.c
index bd0650ac4..bedd3de61 100644
--- a/src/decoder/_ogg_common.c
+++ b/src/decoder/_ogg_common.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/_ogg_common.h b/src/decoder/_ogg_common.h
index f8446c69c..85e4ebba6 100644
--- a/src/decoder/_ogg_common.h
+++ b/src/decoder/_ogg_common.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/audiofile_decoder_plugin.c b/src/decoder/audiofile_decoder_plugin.c
index b099cf706..c862168f8 100644
--- a/src/decoder/audiofile_decoder_plugin.c
+++ b/src/decoder/audiofile_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/faad_decoder_plugin.c b/src/decoder/faad_decoder_plugin.c
index 8f932ad58..02c72a4a1 100644
--- a/src/decoder/faad_decoder_plugin.c
+++ b/src/decoder/faad_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/ffmpeg_decoder_plugin.c b/src/decoder/ffmpeg_decoder_plugin.c
index ba47b2c2c..b4f1f0b51 100644
--- a/src/decoder/ffmpeg_decoder_plugin.c
+++ b/src/decoder/ffmpeg_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -32,22 +32,14 @@
#include <sys/stat.h>
#include <unistd.h>
-#ifdef OLD_FFMPEG_INCLUDES
-#include <avcodec.h>
-#include <avformat.h>
-#include <avio.h>
-#else
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavutil/log.h>
-#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "ffmpeg"
-#ifndef OLD_FFMPEG_INCLUDES
-
static GLogLevelFlags
level_ffmpeg_to_glib(int level)
{
@@ -79,7 +71,6 @@ mpd_ffmpeg_log_callback(G_GNUC_UNUSED void *ptr, int level,
}
}
-#endif /* !OLD_FFMPEG_INCLUDES */
#ifndef AV_VERSION_INT
#define AV_VERSION_INT(a, b, c) (a<<16 | b<<8 | c)
@@ -126,11 +117,19 @@ mpd_ffmpeg_stream_open(struct decoder *decoder, struct input_stream *input)
struct mpd_ffmpeg_stream *stream = g_new(struct mpd_ffmpeg_stream, 1);
stream->decoder = decoder;
stream->input = input;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52,101,0)
+ stream->io = avio_alloc_context(stream->buffer, sizeof(stream->buffer),
+ false, stream,
+ mpd_ffmpeg_stream_read, NULL,
+ input->seekable
+ ? mpd_ffmpeg_stream_seek : NULL);
+#else
stream->io = av_alloc_put_byte(stream->buffer, sizeof(stream->buffer),
false, stream,
mpd_ffmpeg_stream_read, NULL,
input->seekable
? mpd_ffmpeg_stream_seek : NULL);
+#endif
if (stream->io == NULL) {
g_free(stream);
return NULL;
@@ -176,9 +175,7 @@ mpd_ffmpeg_stream_close(struct mpd_ffmpeg_stream *stream)
static bool
ffmpeg_init(G_GNUC_UNUSED const struct config_param *param)
{
-#ifndef OLD_FFMPEG_INCLUDES
av_log_set_callback(mpd_ffmpeg_log_callback);
-#endif
av_register_all();
return true;
@@ -299,7 +296,6 @@ ffmpeg_send_packet(struct decoder *decoder, struct input_stream *is,
static enum sample_format
ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
{
-#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(51, 41, 0)
switch (codec_context->sample_fmt) {
case SAMPLE_FMT_S16:
return SAMPLE_FORMAT_S16;
@@ -312,10 +308,6 @@ ffmpeg_sample_format(G_GNUC_UNUSED const AVCodecContext *codec_context)
codec_context->sample_fmt);
return SAMPLE_FORMAT_UNDEFINED;
}
-#else
- /* XXX fixme 16-bit for older ffmpeg (13 Aug 2007) */
- return SAMPLE_FORMAT_S16;
-#endif
}
static AVInputFormat *
@@ -471,7 +463,6 @@ ffmpeg_decode(struct decoder *decoder, struct input_stream *input)
mpd_ffmpeg_stream_close(stream);
}
-#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
typedef struct ffmpeg_tag_map {
enum tag_type type;
const char *name;
@@ -522,8 +513,6 @@ ffmpeg_copy_metadata(struct tag *tag,
return mt != NULL;
}
-#endif
-
//no tag reading in ffmpeg, check if playable
static struct tag *
ffmpeg_stream_tag(struct input_stream *is)
@@ -555,8 +544,9 @@ ffmpeg_stream_tag(struct input_stream *is)
? f->duration / AV_TIME_BASE
: 0;
-#if LIBAVFORMAT_VERSION_INT >= ((52<<16)+(31<<8)+0)
+#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52,101,0)
av_metadata_conv(f, NULL, f->iformat->metadata_conv);
+#endif
for (unsigned i = 0; i < sizeof(ffmpeg_tag_maps)/sizeof(ffmpeg_tag_map); i++) {
int idx = ffmpeg_find_audio_stream(f);
@@ -564,31 +554,6 @@ ffmpeg_stream_tag(struct input_stream *is)
if (idx >= 0)
ffmpeg_copy_metadata(tag, f->streams[idx]->metadata, ffmpeg_tag_maps[i]);
}
-#else
- if (f->author[0])
- tag_add_item(tag, TAG_ARTIST, f->author);
- if (f->title[0])
- tag_add_item(tag, TAG_TITLE, f->title);
- if (f->album[0])
- tag_add_item(tag, TAG_ALBUM, f->album);
-
- if (f->track > 0) {
- char buffer[16];
- snprintf(buffer, sizeof(buffer), "%d", f->track);
- tag_add_item(tag, TAG_TRACK, buffer);
- }
-
- if (f->comment[0])
- tag_add_item(tag, TAG_COMMENT, f->comment);
- if (f->genre[0])
- tag_add_item(tag, TAG_GENRE, f->genre);
- if (f->year > 0) {
- char buffer[16];
- snprintf(buffer, sizeof(buffer), "%d", f->year);
- tag_add_item(tag, TAG_DATE, buffer);
- }
-
-#endif
av_close_input_stream(f);
mpd_ffmpeg_stream_close(stream);
diff --git a/src/decoder/flac_compat.h b/src/decoder/flac_compat.h
index d597690a0..9a30acc26 100644
--- a/src/decoder/flac_compat.h
+++ b/src/decoder/flac_compat.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/flac_decoder_plugin.c b/src/decoder/flac_decoder_plugin.c
index 9d980b79d..ca9cd5968 100644
--- a/src/decoder/flac_decoder_plugin.c
+++ b/src/decoder/flac_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/flac_metadata.c b/src/decoder/flac_metadata.c
index 5b94fd426..a19220572 100644
--- a/src/decoder/flac_metadata.c
+++ b/src/decoder/flac_metadata.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/flac_metadata.h b/src/decoder/flac_metadata.h
index e52b0fb82..01bc1924a 100644
--- a/src/decoder/flac_metadata.h
+++ b/src/decoder/flac_metadata.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/flac_pcm.c b/src/decoder/flac_pcm.c
index bf6e2612c..3b56d50bd 100644
--- a/src/decoder/flac_pcm.c
+++ b/src/decoder/flac_pcm.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/flac_pcm.h b/src/decoder/flac_pcm.h
index bccfc645c..a931998c1 100644
--- a/src/decoder/flac_pcm.h
+++ b/src/decoder/flac_pcm.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/fluidsynth_decoder_plugin.c b/src/decoder/fluidsynth_decoder_plugin.c
index b9a2d0d99..814a7b554 100644
--- a/src/decoder/fluidsynth_decoder_plugin.c
+++ b/src/decoder/fluidsynth_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -102,7 +102,7 @@ fluidsynth_file_decode(struct decoder *decoder, const char *path_fs)
fluid_player_t *player;
char *path_dup;
int ret;
- Timer *timer;
+ struct timer *timer;
enum decoder_command cmd;
soundfont_path =
diff --git a/src/decoder/mad_decoder_plugin.c b/src/decoder/mad_decoder_plugin.c
index 2c2906c5c..8f77052f7 100644
--- a/src/decoder/mad_decoder_plugin.c
+++ b/src/decoder/mad_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/mikmod_decoder_plugin.c b/src/decoder/mikmod_decoder_plugin.c
index 91478e86f..9dd5a79b6 100644
--- a/src/decoder/mikmod_decoder_plugin.c
+++ b/src/decoder/mikmod_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/modplug_decoder_plugin.c b/src/decoder/modplug_decoder_plugin.c
index 037c2fd74..341b00927 100644
--- a/src/decoder/modplug_decoder_plugin.c
+++ b/src/decoder/modplug_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/mp4ff_decoder_plugin.c b/src/decoder/mp4ff_decoder_plugin.c
index 861b08194..38ae5793a 100644
--- a/src/decoder/mp4ff_decoder_plugin.c
+++ b/src/decoder/mp4ff_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/mpcdec_decoder_plugin.c b/src/decoder/mpcdec_decoder_plugin.c
index eaf470a40..f31dcdb99 100644
--- a/src/decoder/mpcdec_decoder_plugin.c
+++ b/src/decoder/mpcdec_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/mpg123_decoder_plugin.c b/src/decoder/mpg123_decoder_plugin.c
index 7b48ebfaf..224f3db2a 100644
--- a/src/decoder/mpg123_decoder_plugin.c
+++ b/src/decoder/mpg123_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include <glib.h>
#include <mpg123.h>
+#include <stdio.h>
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "mpg123"
@@ -105,6 +106,7 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
int error;
off_t num_samples;
enum decoder_command cmd;
+ struct mpg123_frameinfo info;
/* open the file */
@@ -124,10 +126,25 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
/* tell MPD core we're ready */
- decoder_initialized(decoder, &audio_format, false,
+ decoder_initialized(decoder, &audio_format, true,
(float)num_samples /
(float)audio_format.sample_rate);
+ if (mpg123_info(handle, &info) != MPG123_OK) {
+ info.vbr = MPG123_CBR;
+ info.bitrate = 0;
+ }
+
+ switch (info.vbr) {
+ case MPG123_ABR:
+ info.bitrate = info.abr_rate;
+ break;
+ case MPG123_CBR:
+ break;
+ default:
+ info.bitrate = 0;
+ }
+
/* the decoder main loop */
do {
@@ -144,11 +161,30 @@ mpd_mpg123_file_decode(struct decoder *decoder, const char *path_fs)
break;
}
+ /* update bitrate for ABR/VBR */
+ if (info.vbr != MPG123_CBR) {
+ /* FIXME: maybe skip, as too expensive? */
+ /* FIXME: maybe, (info.vbr == MPG123_VBR) ? */
+ if (mpg123_info (handle, &info) != MPG123_OK)
+ info.bitrate = 0;
+ }
+
/* send to MPD */
- cmd = decoder_data(decoder, NULL, buffer, nbytes, 0);
+ cmd = decoder_data(decoder, NULL, buffer, nbytes, info.bitrate);
- /* seeking not yet implemented */
+ if (cmd == DECODE_COMMAND_SEEK) {
+ off_t c = decoder_seek_where(decoder)*audio_format.sample_rate;
+ c = mpg123_seek(handle, c, SEEK_SET);
+ if (c < 0)
+ decoder_seek_error(decoder);
+ else {
+ decoder_command_finished(decoder);
+ decoder_timestamp(decoder, c/(double)audio_format.sample_rate);
+ }
+
+ cmd = DECODE_COMMAND_NONE;
+ }
} while (cmd == DECODE_COMMAND_NONE);
/* cleanup */
diff --git a/src/decoder/oggflac_decoder_plugin.c b/src/decoder/oggflac_decoder_plugin.c
deleted file mode 100644
index 7e5f48318..000000000
--- a/src/decoder/oggflac_decoder_plugin.c
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * 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.
- */
-
-/*
- * OggFLAC support (half-stolen from flac_plugin.c :))
- */
-
-#include "config.h" /* must be first for large file support */
-#include "_flac_common.h"
-#include "_ogg_common.h"
-#include "flac_metadata.h"
-
-#include <glib.h>
-#include <OggFLAC/seekable_stream_decoder.h>
-#include <assert.h>
-#include <unistd.h>
-
-static void oggflac_cleanup(OggFLAC__SeekableStreamDecoder * decoder)
-{
- if (decoder)
- OggFLAC__seekable_stream_decoder_delete(decoder);
-}
-
-static OggFLAC__SeekableStreamDecoderReadStatus of_read_cb(G_GNUC_UNUSED const
- OggFLAC__SeekableStreamDecoder
- * decoder,
- FLAC__byte buf[],
- unsigned *bytes,
- void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
- size_t r;
-
- r = decoder_read(data->decoder, data->input_stream,
- (void *)buf, *bytes);
- *bytes = r;
-
- if (r == 0 && !input_stream_eof(data->input_stream) &&
- decoder_get_command(data->decoder) == DECODE_COMMAND_NONE)
- return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
-
- return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
-}
-
-static OggFLAC__SeekableStreamDecoderSeekStatus of_seek_cb(G_GNUC_UNUSED const
- OggFLAC__SeekableStreamDecoder
- * decoder,
- FLAC__uint64 offset,
- void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (!input_stream_seek(data->input_stream, offset, SEEK_SET, NULL))
- return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
-
- return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
-}
-
-static OggFLAC__SeekableStreamDecoderTellStatus of_tell_cb(G_GNUC_UNUSED const
- OggFLAC__SeekableStreamDecoder
- * decoder,
- FLAC__uint64 *
- offset, void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- *offset = (long)(data->input_stream->offset);
-
- return OggFLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
-}
-
-static OggFLAC__SeekableStreamDecoderLengthStatus of_length_cb(G_GNUC_UNUSED const
- OggFLAC__SeekableStreamDecoder
- * decoder,
- FLAC__uint64 *
- length,
- void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- if (data->input_stream->size < 0)
- return OggFLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_ERROR;
-
- *length = (size_t) (data->input_stream->size);
-
- return OggFLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
-}
-
-static FLAC__bool of_EOF_cb(G_GNUC_UNUSED const OggFLAC__SeekableStreamDecoder * decoder,
- void *fdata)
-{
- struct flac_data *data = (struct flac_data *) fdata;
-
- return (decoder_get_command(data->decoder) != DECODE_COMMAND_NONE &&
- decoder_get_command(data->decoder) != DECODE_COMMAND_SEEK) ||
- input_stream_eof(data->input_stream);
-}
-
-static void of_error_cb(G_GNUC_UNUSED const OggFLAC__SeekableStreamDecoder * decoder,
- FLAC__StreamDecoderErrorStatus status, void *fdata)
-{
- flac_error_common_cb("oggflac", status, (struct flac_data *) fdata);
-}
-
-static void oggflacPrintErroredState(OggFLAC__SeekableStreamDecoderState state)
-{
- switch (state) {
- case OggFLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
- g_warning("oggflac allocation error\n");
- break;
- case OggFLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
- g_warning("oggflac read error\n");
- break;
- case OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
- g_warning("oggflac seek error\n");
- break;
- case OggFLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
- g_warning("oggflac seekable stream error\n");
- break;
- case OggFLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
- g_warning("oggflac decoder already initialized\n");
- break;
- case OggFLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
- g_warning("invalid oggflac callback\n");
- break;
- case OggFLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
- g_warning("oggflac decoder uninitialized\n");
- break;
- case OggFLAC__SEEKABLE_STREAM_DECODER_OK:
- case OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING:
- case OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
- break;
- }
-}
-
-static FLAC__StreamDecoderWriteStatus
-oggflac_write_cb(G_GNUC_UNUSED const OggFLAC__SeekableStreamDecoder *decoder,
- const FLAC__Frame *frame, const FLAC__int32 *const buf[],
- void *vdata)
-{
- struct flac_data *data = (struct flac_data *) vdata;
-
- return flac_common_write(data, frame, buf, 0);
-}
-
-/* used by TagDup */
-static void of_metadata_dup_cb(G_GNUC_UNUSED const OggFLAC__SeekableStreamDecoder * decoder,
- const FLAC__StreamMetadata * block, void *vdata)
-{
- struct flac_data *data = (struct flac_data *) vdata;
-
- assert(data->tag != NULL);
-
- flac_tag_apply_metadata(data->tag, NULL, block);
-}
-
-/* used by decode */
-static void of_metadata_decode_cb(G_GNUC_UNUSED const OggFLAC__SeekableStreamDecoder * dec,
- const FLAC__StreamMetadata * block,
- void *vdata)
-{
- flac_metadata_common_cb(block, (struct flac_data *) vdata);
-}
-
-static OggFLAC__SeekableStreamDecoder *
-full_decoder_init_and_read_metadata(struct flac_data *data,
- unsigned int metadata_only)
-{
- OggFLAC__SeekableStreamDecoder *decoder = NULL;
- unsigned int s = 1;
-
- if (!(decoder = OggFLAC__seekable_stream_decoder_new()))
- return NULL;
-
- if (metadata_only) {
- s &= OggFLAC__seekable_stream_decoder_set_metadata_callback
- (decoder, of_metadata_dup_cb);
- s &= OggFLAC__seekable_stream_decoder_set_metadata_respond
- (decoder, FLAC__METADATA_TYPE_STREAMINFO);
- } else {
- s &= OggFLAC__seekable_stream_decoder_set_metadata_callback
- (decoder, of_metadata_decode_cb);
- }
-
- s &= OggFLAC__seekable_stream_decoder_set_read_callback(decoder,
- of_read_cb);
- s &= OggFLAC__seekable_stream_decoder_set_seek_callback(decoder,
- of_seek_cb);
- s &= OggFLAC__seekable_stream_decoder_set_tell_callback(decoder,
- of_tell_cb);
- s &= OggFLAC__seekable_stream_decoder_set_length_callback(decoder,
- of_length_cb);
- s &= OggFLAC__seekable_stream_decoder_set_eof_callback(decoder,
- of_EOF_cb);
- s &= OggFLAC__seekable_stream_decoder_set_write_callback(decoder,
- oggflac_write_cb);
- s &= OggFLAC__seekable_stream_decoder_set_metadata_respond(decoder,
- FLAC__METADATA_TYPE_VORBIS_COMMENT);
- s &= OggFLAC__seekable_stream_decoder_set_error_callback(decoder,
- of_error_cb);
- s &= OggFLAC__seekable_stream_decoder_set_client_data(decoder,
- (void *)data);
-
- if (!s) {
- g_warning("oggflac problem before init()\n");
- goto fail;
- }
- if (OggFLAC__seekable_stream_decoder_init(decoder) !=
- OggFLAC__SEEKABLE_STREAM_DECODER_OK) {
- g_warning("oggflac problem doing init()\n");
- goto fail;
- }
- if (!OggFLAC__seekable_stream_decoder_process_until_end_of_metadata
- (decoder)) {
- g_warning("oggflac problem reading metadata\n");
- goto fail;
- }
-
- return decoder;
-
-fail:
- oggflacPrintErroredState(OggFLAC__seekable_stream_decoder_get_state
- (decoder));
- OggFLAC__seekable_stream_decoder_delete(decoder);
- return NULL;
-}
-
-/* public functions: */
-static struct tag *
-oggflac_stream_tag(struct input_stream *is)
-{
- OggFLAC__SeekableStreamDecoder *decoder;
- struct flac_data data;
- struct tag *tag;
-
- if (ogg_stream_type_detect(is) != FLAC)
- return NULL;
-
- /* rewind the stream, because ogg_stream_type_detect() has
- moved it */
- input_stream_seek(is, 0, SEEK_SET, NULL);
-
- flac_data_init(&data, NULL, is);
-
- data.tag = tag_new();
-
- /* errors here won't matter,
- * data.tag will be set or unset, that's all we care about */
- decoder = full_decoder_init_and_read_metadata(&data, 1);
-
- oggflac_cleanup(decoder);
-
- if (tag_is_defined(data.tag)) {
- tag = data.tag;
- data.tag = NULL;
- } else
- tag = NULL;
-
- flac_data_deinit(&data);
-
- return tag;
-}
-
-static void
-oggflac_decode(struct decoder * mpd_decoder, struct input_stream *input_stream)
-{
- OggFLAC__SeekableStreamDecoder *decoder = NULL;
- struct flac_data data;
- struct audio_format audio_format;
-
- if (ogg_stream_type_detect(input_stream) != FLAC)
- return;
-
- /* rewind the stream, because ogg_stream_type_detect() has
- moved it */
- input_stream_seek(input_stream, 0, SEEK_SET, NULL);
-
- flac_data_init(&data, mpd_decoder, input_stream);
-
- if (!(decoder = full_decoder_init_and_read_metadata(&data, 0))) {
- goto fail;
- }
-
- if (!data.initialized)
- goto fail;
-
- decoder_initialized(mpd_decoder, &audio_format,
- input_stream->seekable,
- (float)data.total_frames /
- (float)data.audio_format.sample_rate);
-
- while (true) {
- OggFLAC__seekable_stream_decoder_process_single(decoder);
- if (OggFLAC__seekable_stream_decoder_get_state(decoder) !=
- OggFLAC__SEEKABLE_STREAM_DECODER_OK) {
- break;
- }
- if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_SEEK) {
- FLAC__uint64 seek_sample = decoder_seek_where(mpd_decoder) *
- data.audio_format.sample_rate;
- if (OggFLAC__seekable_stream_decoder_seek_absolute
- (decoder, seek_sample)) {
- data.next_frame = seek_sample;
- data.position = 0;
- decoder_command_finished(mpd_decoder);
- } else
- decoder_seek_error(mpd_decoder);
- }
- }
-
- if (decoder_get_command(mpd_decoder) == DECODE_COMMAND_NONE) {
- oggflacPrintErroredState
- (OggFLAC__seekable_stream_decoder_get_state(decoder));
- OggFLAC__seekable_stream_decoder_finish(decoder);
- }
-
-fail:
- oggflac_cleanup(decoder);
- flac_data_deinit(&data);
-}
-
-static const char *const oggflac_suffixes[] = { "ogg", "oga", NULL };
-static const char *const oggflac_mime_types[] = {
- "application/ogg",
- "application/x-ogg",
- "audio/ogg",
- "audio/x-ogg",
- "audio/x-flac+ogg",
- NULL
-};
-
-const struct decoder_plugin oggflac_decoder_plugin = {
- .name = "oggflac",
- .stream_decode = oggflac_decode,
- .stream_tag = oggflac_stream_tag,
- .suffixes = oggflac_suffixes,
- .mime_types = oggflac_mime_types
-};
diff --git a/src/decoder/pcm_decoder_plugin.c b/src/decoder/pcm_decoder_plugin.c
new file mode 100644
index 000000000..c8340ab67
--- /dev/null
+++ b/src/decoder/pcm_decoder_plugin.c
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "decoder/pcm_decoder_plugin.h"
+#include "decoder_api.h"
+
+#include <glib.h>
+#include <unistd.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "pcm"
+
+static void
+pcm_stream_decode(struct decoder *decoder, struct input_stream *is)
+{
+ static const struct audio_format audio_format = {
+ .sample_rate = 44100,
+ .format = SAMPLE_FORMAT_S16,
+ .channels = 2,
+ };
+ GError *error = NULL;
+ enum decoder_command cmd;
+
+ double time_to_size = audio_format_time_to_size(&audio_format);
+
+ float total_time = -1;
+ if (is->size >= 0)
+ total_time = is->size / time_to_size;
+
+ decoder_initialized(decoder, &audio_format, is->seekable, total_time);
+
+ do {
+ char buffer[4096];
+
+ size_t nbytes = decoder_read(decoder, is,
+ buffer, sizeof(buffer));
+
+ if (nbytes == 0 && input_stream_eof(is))
+ break;
+
+ cmd = nbytes > 0
+ ? decoder_data(decoder, is,
+ buffer, nbytes, 0)
+ : decoder_get_command(decoder);
+ if (cmd == DECODE_COMMAND_SEEK) {
+ goffset offset = (goffset)(time_to_size *
+ decoder_seek_where(decoder));
+ if (input_stream_seek(is, offset, SEEK_SET, &error)) {
+ decoder_command_finished(decoder);
+ } else {
+ g_warning("seeking failed: %s", error->message);
+ g_error_free(error);
+ decoder_seek_error(decoder);
+ }
+
+ cmd = DECODE_COMMAND_NONE;
+ }
+ } while (cmd == DECODE_COMMAND_NONE);
+}
+
+static const char *const pcm_mime_types[] = {
+ /* for streams obtained by the cdio_paranoia input plugin */
+ "audio/x-mpd-cdda-pcm",
+ NULL
+};
+
+const struct decoder_plugin pcm_decoder_plugin = {
+ .name = "pcm",
+ .stream_decode = pcm_stream_decode,
+ .mime_types = pcm_mime_types,
+};
diff --git a/src/decoder/pcm_decoder_plugin.h b/src/decoder/pcm_decoder_plugin.h
new file mode 100644
index 000000000..11df80155
--- /dev/null
+++ b/src/decoder/pcm_decoder_plugin.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+/** \file
+ *
+ * Not really a decoder; this plugin forwards its input data "as-is".
+ *
+ * It was written only to support the "cdio_paranoia" input plugin,
+ * which does not need a decoder.
+ */
+
+#ifndef MPD_DECODER_PCM_H
+#define MPD_DECODER_PCM_H
+
+extern const struct decoder_plugin pcm_decoder_plugin;
+
+#endif
diff --git a/src/decoder/sidplay_decoder_plugin.cxx b/src/decoder/sidplay_decoder_plugin.cxx
index 6fceeb30f..9aeec8b51 100644
--- a/src/decoder/sidplay_decoder_plugin.cxx
+++ b/src/decoder/sidplay_decoder_plugin.cxx
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/sndfile_decoder_plugin.c b/src/decoder/sndfile_decoder_plugin.c
index af68f117d..dbe9bf067 100644
--- a/src/decoder/sndfile_decoder_plugin.c
+++ b/src/decoder/sndfile_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/vorbis_decoder_plugin.c b/src/decoder/vorbis_decoder_plugin.c
index 0a3944ad6..c130005a7 100644
--- a/src/decoder/vorbis_decoder_plugin.c
+++ b/src/decoder/vorbis_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/wavpack_decoder_plugin.c b/src/decoder/wavpack_decoder_plugin.c
index 24d0c1703..200bf6455 100644
--- a/src/decoder/wavpack_decoder_plugin.c
+++ b/src/decoder/wavpack_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder/wildmidi_decoder_plugin.c b/src/decoder/wildmidi_decoder_plugin.c
index 66e6c61cf..5bc36b4e3 100644
--- a/src/decoder/wildmidi_decoder_plugin.c
+++ b/src/decoder/wildmidi_decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_api.c b/src/decoder_api.c
index fe34ea34a..239ef6daf 100644
--- a/src/decoder_api.c
+++ b/src/decoder_api.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -21,7 +21,6 @@
#include "decoder_api.h"
#include "decoder_internal.h"
#include "decoder_control.h"
-#include "player_control.h"
#include "audio.h"
#include "song.h"
#include "buffer.h"
@@ -63,10 +62,9 @@ decoder_initialized(struct decoder *decoder,
decoder_lock(dc);
dc->state = DECODE_STATE_DECODE;
+ g_cond_signal(dc->client_cond);
decoder_unlock(dc);
- player_lock_signal();
-
g_debug("audio_format=%s, seekable=%s",
audio_format_to_string(&dc->in_audio_format, &af_string),
seekable ? "true" : "false");
@@ -115,9 +113,8 @@ decoder_command_finished(struct decoder *decoder)
}
dc->command = DECODE_COMMAND_NONE;
+ g_cond_signal(dc->client_cond);
decoder_unlock(dc);
-
- player_lock_signal();
}
double decoder_seek_where(G_GNUC_UNUSED struct decoder * decoder)
@@ -205,8 +202,7 @@ decoder_timestamp(struct decoder *decoder, double t)
* (decoder.chunk) if there is one.
*/
static enum decoder_command
-do_send_tag(struct decoder *decoder, struct input_stream *is,
- const struct tag *tag)
+do_send_tag(struct decoder *decoder, const struct tag *tag)
{
struct music_chunk *chunk;
@@ -214,12 +210,12 @@ do_send_tag(struct decoder *decoder, struct input_stream *is,
/* there is a partial chunk - flush it, we want the
tag in a new chunk */
decoder_flush_chunk(decoder);
- player_lock_signal();
+ g_cond_signal(decoder->dc->client_cond);
}
assert(decoder->chunk == NULL);
- chunk = decoder_get_chunk(decoder, is);
+ chunk = decoder_get_chunk(decoder);
if (chunk == NULL) {
assert(decoder->dc->command != DECODE_COMMAND_NONE);
return decoder->dc->command;
@@ -286,11 +282,11 @@ decoder_data(struct decoder *decoder,
tag = tag_merge(decoder->decoder_tag,
decoder->stream_tag);
- cmd = do_send_tag(decoder, is, tag);
+ cmd = do_send_tag(decoder, tag);
tag_free(tag);
} else
/* send only the stream tag */
- cmd = do_send_tag(decoder, is, decoder->stream_tag);
+ cmd = do_send_tag(decoder, decoder->stream_tag);
if (cmd != DECODE_COMMAND_NONE)
return cmd;
@@ -316,7 +312,7 @@ decoder_data(struct decoder *decoder,
size_t nbytes;
bool full;
- chunk = decoder_get_chunk(decoder, is);
+ chunk = decoder_get_chunk(decoder);
if (chunk == NULL) {
assert(dc->command != DECODE_COMMAND_NONE);
return dc->command;
@@ -329,7 +325,7 @@ decoder_data(struct decoder *decoder,
if (dest == NULL) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
- player_lock_signal();
+ g_cond_signal(dc->client_cond);
continue;
}
@@ -348,7 +344,7 @@ decoder_data(struct decoder *decoder,
if (full) {
/* the chunk is full, flush it */
decoder_flush_chunk(decoder);
- player_lock_signal();
+ g_cond_signal(dc->client_cond);
}
data += nbytes;
@@ -395,11 +391,11 @@ decoder_tag(G_GNUC_UNUSED struct decoder *decoder, struct input_stream *is,
struct tag *merged;
merged = tag_merge(decoder->stream_tag, decoder->decoder_tag);
- cmd = do_send_tag(decoder, is, merged);
+ cmd = do_send_tag(decoder, merged);
tag_free(merged);
} else
/* send only the decoder tag */
- cmd = do_send_tag(decoder, is, tag);
+ cmd = do_send_tag(decoder, tag);
return cmd;
}
@@ -432,7 +428,7 @@ decoder_replay_gain(struct decoder *decoder,
replay gain values affect the following
samples */
decoder_flush_chunk(decoder);
- player_lock_signal();
+ g_cond_signal(decoder->dc->client_cond);
}
} else
decoder->replay_gain_serial = 0;
diff --git a/src/decoder_api.h b/src/decoder_api.h
index 8b5f3d82b..9d032b451 100644
--- a/src/decoder_api.h
+++ b/src/decoder_api.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_buffer.c b/src/decoder_buffer.c
index 8f8eb8545..fcb135976 100644
--- a/src/decoder_buffer.c
+++ b/src/decoder_buffer.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_buffer.h b/src/decoder_buffer.h
index b6051e122..77eff5dd1 100644
--- a/src/decoder_buffer.h
+++ b/src/decoder_buffer.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_command.h b/src/decoder_command.h
index 4a2e49f3e..795e13fb2 100644
--- a/src/decoder_command.h
+++ b/src/decoder_command.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_control.c b/src/decoder_control.c
index a5e6e4ad3..685db6c22 100644
--- a/src/decoder_control.c
+++ b/src/decoder_control.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,7 +19,6 @@
#include "config.h"
#include "decoder_control.h"
-#include "player_control.h"
#include "pipe.h"
#include <assert.h>
@@ -27,13 +26,16 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "decoder_control"
-void
-dc_init(struct decoder_control *dc)
+struct decoder_control *
+dc_new(GCond *client_cond)
{
+ struct decoder_control *dc = g_new(struct decoder_control, 1);
+
dc->thread = NULL;
dc->mutex = g_mutex_new();
dc->cond = g_cond_new();
+ dc->client_cond = client_cond;
dc->state = DECODE_STATE_STOP;
dc->command = DECODE_COMMAND_NONE;
@@ -43,34 +45,26 @@ dc_init(struct decoder_control *dc)
dc->mixramp_start = NULL;
dc->mixramp_end = NULL;
dc->mixramp_prev_end = NULL;
+
+ return dc;
}
void
-dc_deinit(struct decoder_control *dc)
+dc_free(struct decoder_control *dc)
{
g_cond_free(dc->cond);
g_mutex_free(dc->mutex);
g_free(dc->mixramp_start);
g_free(dc->mixramp_end);
g_free(dc->mixramp_prev_end);
- dc->mixramp_start = NULL;
- dc->mixramp_end = NULL;
- dc->mixramp_prev_end = NULL;
+ g_free(dc);
}
static void
dc_command_wait_locked(struct decoder_control *dc)
{
while (dc->command != DECODE_COMMAND_NONE)
- player_wait_decoder(dc);
-}
-
-void
-dc_command_wait(struct decoder_control *dc)
-{
- decoder_lock(dc);
- dc_command_wait_locked(dc);
- decoder_unlock(dc);
+ g_cond_wait(dc->client_cond, dc->mutex);
}
static void
diff --git a/src/decoder_control.h b/src/decoder_control.h
index 449e974b7..e1a718a59 100644
--- a/src/decoder_control.h
+++ b/src/decoder_control.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -58,6 +58,12 @@ struct decoder_control {
*/
GCond *cond;
+ /**
+ * The trigger of this object's client. It is signalled
+ * whenever an event occurs.
+ */
+ GCond *client_cond;
+
enum decoder_state state;
enum decoder_command command;
@@ -97,11 +103,12 @@ struct decoder_control {
char *mixramp_prev_end;
};
-void
-dc_init(struct decoder_control *dc);
+G_GNUC_MALLOC
+struct decoder_control *
+dc_new(GCond *client_cond);
void
-dc_deinit(struct decoder_control *dc);
+dc_free(struct decoder_control *dc);
/**
* Locks the #decoder_control object.
@@ -217,9 +224,6 @@ decoder_current_song(const struct decoder_control *dc)
return NULL;
}
-void
-dc_command_wait(struct decoder_control *dc);
-
/**
* Start the decoder.
*
diff --git a/src/decoder_internal.c b/src/decoder_internal.c
index 990d728e9..bc349f2ff 100644
--- a/src/decoder_internal.c
+++ b/src/decoder_internal.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,6 @@
#include "config.h"
#include "decoder_internal.h"
#include "decoder_control.h"
-#include "player_control.h"
#include "pipe.h"
#include "input_stream.h"
#include "buffer.h"
@@ -29,43 +28,19 @@
#include <assert.h>
/**
- * This is a wrapper for input_stream_buffer(). It assumes that the
- * decoder is currently locked, and temporarily unlocks it while
- * calling input_stream_buffer(). We shouldn't hold the lock during a
- * potentially blocking operation.
- */
-static bool
-decoder_input_buffer(struct decoder_control *dc, struct input_stream *is)
-{
- GError *error = NULL;
- int ret;
-
- decoder_unlock(dc);
- ret = input_stream_buffer(is, &error);
- if (ret < 0) {
- g_warning("%s", error->message);
- g_error_free(error);
- }
-
- decoder_lock(dc);
-
- return ret > 0;
-}
-
-/**
* All chunks are full of decoded data; wait for the player to free
* one.
*/
static enum decoder_command
-need_chunks(struct decoder_control *dc, struct input_stream *is, bool do_wait)
+need_chunks(struct decoder_control *dc, bool do_wait)
{
if (dc->command == DECODE_COMMAND_STOP ||
dc->command == DECODE_COMMAND_SEEK)
return dc->command;
- if ((is == NULL || !decoder_input_buffer(dc, is)) && do_wait) {
+ if (do_wait) {
decoder_wait(dc);
- player_signal();
+ g_cond_signal(dc->client_cond);
return dc->command;
}
@@ -74,7 +49,7 @@ need_chunks(struct decoder_control *dc, struct input_stream *is, bool do_wait)
}
struct music_chunk *
-decoder_get_chunk(struct decoder *decoder, struct input_stream *is)
+decoder_get_chunk(struct decoder *decoder)
{
struct decoder_control *dc = decoder->dc;
enum decoder_command cmd;
@@ -97,7 +72,7 @@ decoder_get_chunk(struct decoder *decoder, struct input_stream *is)
}
decoder_lock(dc);
- cmd = need_chunks(dc, is, true);
+ cmd = need_chunks(dc, true);
decoder_unlock(dc);
} while (cmd == DECODE_COMMAND_NONE);
diff --git a/src/decoder_internal.h b/src/decoder_internal.h
index 9e7e2037a..78a783f5e 100644
--- a/src/decoder_internal.h
+++ b/src/decoder_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -70,7 +70,7 @@ struct decoder {
* @return the chunk, or NULL if we have received a decoder command
*/
struct music_chunk *
-decoder_get_chunk(struct decoder *decoder, struct input_stream *is);
+decoder_get_chunk(struct decoder *decoder);
/**
* Flushes the current chunk.
diff --git a/src/decoder_list.c b/src/decoder_list.c
index d76050023..11da3f63c 100644
--- a/src/decoder_list.c
+++ b/src/decoder_list.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
#include "utils.h"
#include "conf.h"
#include "mpd_error.h"
+#include "decoder/pcm_decoder_plugin.h"
#include <glib.h>
@@ -57,7 +58,7 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef ENABLE_VORBIS_DECODER
&vorbis_decoder_plugin,
#endif
-#if defined(HAVE_FLAC) || defined(HAVE_OGGFLAC)
+#if defined(HAVE_FLAC)
&oggflac_decoder_plugin,
#endif
#ifdef HAVE_FLAC
@@ -102,6 +103,7 @@ const struct decoder_plugin *const decoder_plugins[] = {
#ifdef HAVE_GME
&gme_decoder_plugin,
#endif
+ &pcm_decoder_plugin,
NULL
};
diff --git a/src/decoder_list.h b/src/decoder_list.h
index 7041db0c9..d259cb195 100644
--- a/src/decoder_list.h
+++ b/src/decoder_list.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_plugin.c b/src/decoder_plugin.c
index 062dad364..d32043f0e 100644
--- a/src/decoder_plugin.c
+++ b/src/decoder_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,7 +19,7 @@
#include "config.h"
#include "decoder_plugin.h"
-#include "utils.h"
+#include "string_util.h"
#include <assert.h>
diff --git a/src/decoder_plugin.h b/src/decoder_plugin.h
index d8371ddb8..0ce1af53e 100644
--- a/src/decoder_plugin.h
+++ b/src/decoder_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_print.c b/src/decoder_print.c
index a1c2da2e5..72c40ac75 100644
--- a/src/decoder_print.c
+++ b/src/decoder_print.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_print.h b/src/decoder_print.h
index 520438871..31713d5d8 100644
--- a/src/decoder_print.h
+++ b/src/decoder_print.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/decoder_thread.c b/src/decoder_thread.c
index 10a796967..320a04638 100644
--- a/src/decoder_thread.c
+++ b/src/decoder_thread.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -26,7 +26,6 @@
#include "decoder_api.h"
#include "replay_gain_ape.h"
#include "input_stream.h"
-#include "player_control.h"
#include "pipe.h"
#include "song.h"
#include "tag.h"
@@ -55,6 +54,22 @@ decoder_lock_get_command(struct decoder_control *dc)
}
/**
+ * Marks the current decoder command as "finished" and notifies the
+ * player thread.
+ *
+ * @param dc the #decoder_control object; must be locked
+ */
+static void
+decoder_command_finished_locked(struct decoder_control *dc)
+{
+ assert(dc->command != DECODE_COMMAND_NONE);
+
+ dc->command = DECODE_COMMAND_NONE;
+
+ g_cond_signal(dc->client_cond);
+}
+
+/**
* Opens the input stream with input_stream_open(), and waits until
* the stream gets ready. If a decoder STOP command is received
* during that, it cancels the operation (but does not close the
@@ -381,9 +396,8 @@ decoder_run_song(struct decoder_control *dc,
decoder.chunk = NULL;
dc->state = DECODE_STATE_START;
- dc->command = DECODE_COMMAND_NONE;
- player_signal();
+ decoder_command_finished_locked(dc);
pcm_convert_init(&decoder.conv_state);
@@ -429,6 +443,7 @@ decoder_run(struct decoder_control *dc)
if (uri == NULL) {
dc->state = DECODE_STATE_ERROR;
+ decoder_command_finished_locked(dc);
return;
}
@@ -461,16 +476,10 @@ decoder_task(gpointer arg)
case DECODE_COMMAND_SEEK:
decoder_run(dc);
-
- dc->command = DECODE_COMMAND_NONE;
-
- player_signal();
break;
case DECODE_COMMAND_STOP:
- dc->command = DECODE_COMMAND_NONE;
-
- player_signal();
+ decoder_command_finished_locked(dc);
break;
case DECODE_COMMAND_NONE:
diff --git a/src/decoder_thread.h b/src/decoder_thread.h
index 28042d7f8..78f12a54a 100644
--- a/src/decoder_thread.h
+++ b/src/decoder_thread.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/despotify_utils.c b/src/despotify_utils.c
new file mode 100644
index 000000000..7555d105d
--- /dev/null
+++ b/src/despotify_utils.c
@@ -0,0 +1,121 @@
+#include <glib.h>
+#include <despotify.h>
+
+#include "tag.h"
+#include "conf.h"
+#include "despotify_utils.h"
+
+static struct despotify_session *g_session;
+static void (*registered_callbacks[8])(struct despotify_session *,
+ int, void *, void *);
+static void *registered_callback_data[8];
+
+static void callback(struct despotify_session* ds, int sig,
+ void* data, G_GNUC_UNUSED void* callback_data)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
+ void (*cb)(struct despotify_session *, int, void *, void *) = registered_callbacks[i];
+ void *cb_data = registered_callback_data[i];
+
+ if (cb)
+ cb(ds, sig, data, cb_data);
+ }
+}
+
+bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
+ void *cb_data)
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
+
+ if (!registered_callbacks[i]) {
+ registered_callbacks[i] = cb;
+ registered_callback_data[i] = cb_data;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *))
+{
+ size_t i;
+
+ for (i = 0; i < sizeof(registered_callbacks) / sizeof(registered_callbacks[0]); i++) {
+
+ if (registered_callbacks[i] == cb) {
+ registered_callbacks[i] = NULL;
+ }
+ }
+}
+
+
+struct tag *mpd_despotify_tag_from_track(struct ds_track *track)
+{
+ char tracknum[20];
+ char comment[80];
+ char date[20];
+ struct tag *tag;
+
+ tag = tag_new();
+
+ if (!track->has_meta_data)
+ return tag;
+
+ g_snprintf(tracknum, sizeof(tracknum), "%d", track->tracknumber);
+ g_snprintf(date, sizeof(date), "%d", track->year);
+ g_snprintf(comment, sizeof(comment), "Bitrate %d Kbps, %sgeo restricted",
+ track->file_bitrate / 1000, track->geo_restricted ? "" : "not ");
+ tag_add_item(tag, TAG_TITLE, track->title);
+ tag_add_item(tag, TAG_ARTIST, track->artist->name);
+ tag_add_item(tag, TAG_TRACK, tracknum);
+ tag_add_item(tag, TAG_ALBUM, track->album);
+ tag_add_item(tag, TAG_DATE, date);
+ tag_add_item(tag, TAG_COMMENT, comment);
+ tag->time = track->length / 1000;
+
+ return tag;
+}
+
+struct despotify_session *mpd_despotify_get_session(void)
+{
+ const char *user;
+ const char *passwd;
+ bool high_bitrate;
+
+ if (g_session)
+ return g_session;
+
+ user = config_get_string(CONF_DESPOTIFY_USER, NULL);
+ passwd = config_get_string(CONF_DESPOTIFY_PASSWORD, NULL);
+ high_bitrate = config_get_bool(CONF_DESPOTIFY_HIGH_BITRATE, true);
+
+ if (user == NULL || passwd == NULL) {
+ g_debug("disabling despotify because account is not configured");
+ return NULL;
+ }
+ if (!despotify_init()) {
+ g_debug("Can't initialize despotify\n");
+ return false;
+ }
+
+ g_session = despotify_init_client(callback, NULL,
+ high_bitrate, true);
+ if (!g_session) {
+ g_debug("Can't initialize despotify client\n");
+ return false;
+ }
+
+ if (!despotify_authenticate(g_session, user, passwd)) {
+ g_debug("Can't authenticate despotify session\n");
+ despotify_exit(g_session);
+ return false;
+ }
+
+ return g_session;
+}
diff --git a/src/despotify_utils.h b/src/despotify_utils.h
new file mode 100644
index 000000000..7e35edc3c
--- /dev/null
+++ b/src/despotify_utils.h
@@ -0,0 +1,67 @@
+/*
+ * 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_DESPOTIFY_H
+#define MPD_DESPOTIFY_H
+
+struct despotify_session;
+struct ds_track;
+
+/**
+ * Return the current despotify session.
+ *
+ * If the session isn't initialized, this function will initialize
+ * it and connect to Spotify.
+ *
+ * @return a pointer to the despotify session, or NULL if it can't
+ * be initialized (e.g., if the configuration isn't supplied)
+ */
+struct despotify_session *mpd_despotify_get_session(void);
+
+/**
+ * Create a MPD tags structure from a spotify track
+ *
+ * @param track the track to convert
+ *
+ * @return a pointer to the filled in tags structure
+ */
+struct tag *mpd_despotify_tag_from_track(struct ds_track *track);
+
+/**
+ * Register a despotify callback.
+ *
+ * Despotify calls this e.g., when a track ends.
+ *
+ * @param cb the callback
+ * @param cb_data the data to pass to the callback
+ *
+ * @return true if the callback could be registered
+ */
+bool mpd_despotify_register_callback(void (*cb)(struct despotify_session *, int, void *, void *),
+ void *cb_data);
+
+/**
+ * Unregister a despotify callback.
+ *
+ * @param cb the callback to unregister.
+ */
+void mpd_despotify_unregister_callback(void (*cb)(struct despotify_session *, int, void *, void *));
+
+#endif
+
diff --git a/src/directory.c b/src/directory.c
index fa15d41b1..ebc4013eb 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
#include "directory.h"
#include "song.h"
#include "path.h"
+#include "db_visitor.h"
#include <glib.h>
@@ -167,28 +168,42 @@ directory_sort(struct directory *directory)
directory_sort(dv->base[i]);
}
-int
-directory_walk(struct directory *directory,
- int (*forEachSong)(struct song *, void *),
- int (*forEachDir)(struct directory *, void *),
- void *data)
+bool
+directory_walk(const struct directory *directory, bool recursive,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r)
{
- struct dirvec *dv = &directory->children;
- int err = 0;
- size_t j;
-
- if (forEachDir && (err = forEachDir(directory, data)) < 0)
- return err;
+ assert(directory != NULL);
+ assert(visitor != NULL);
+ assert(error_r == NULL || *error_r == NULL);
+
+ if (visitor->song != NULL) {
+ const struct songvec *sv = &directory->songs;
+ for (size_t i = 0; i < sv->nr; ++i)
+ if (!visitor->song(sv->base[i], ctx, error_r))
+ return false;
+ }
- if (forEachSong) {
- err = songvec_for_each(&directory->songs, forEachSong, data);
- if (err < 0)
- return err;
+ if (visitor->playlist != NULL) {
+ const struct playlist_vector *pv = &directory->playlists;
+ for (const struct playlist_metadata *i = pv->head;
+ i != NULL; i = i->next)
+ if (!visitor->playlist(i, ctx, error_r))
+ return false;
}
- for (j = 0; err >= 0 && j < dv->nr; ++j)
- err = directory_walk(dv->base[j], forEachSong,
- forEachDir, data);
+ const struct dirvec *dv = &directory->children;
+ for (size_t i = 0; i < dv->nr; ++i) {
+ struct directory *child = dv->base[i];
+
+ if (visitor->directory != NULL &&
+ !visitor->directory(child, ctx, error_r))
+ return false;
+
+ if (recursive &&
+ !directory_walk(child, recursive, visitor, ctx, error_r))
+ return false;
+ }
- return err;
+ return true;
}
diff --git a/src/directory.h b/src/directory.h
index 151cf5423..ce9c96cd2 100644
--- a/src/directory.h
+++ b/src/directory.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -25,6 +25,7 @@
#include "songvec.h"
#include "playlist_vector.h"
+#include <glib.h>
#include <stdbool.h>
#include <sys/types.h>
@@ -33,6 +34,8 @@
#define DEVICE_INARCHIVE (dev_t)(-1)
#define DEVICE_CONTAINER (dev_t)(-2)
+struct db_visitor;
+
struct directory {
struct dirvec children;
struct songvec songs;
@@ -127,10 +130,9 @@ directory_lookup_song(struct directory *directory, const char *uri);
void
directory_sort(struct directory *directory);
-int
-directory_walk(struct directory *directory,
- int (*forEachSong)(struct song *, void *),
- int (*forEachDir)(struct directory *, void *),
- void *data);
+bool
+directory_walk(const struct directory *directory, bool recursive,
+ const struct db_visitor *visitor, void *ctx,
+ GError **error_r);
#endif
diff --git a/src/directory_print.c b/src/directory_print.c
deleted file mode 100644
index 74ff26cb3..000000000
--- a/src/directory_print.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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 "directory_print.h"
-#include "directory.h"
-#include "client.h"
-#include "song_print.h"
-#include "mapper.h"
-#include "decoder_list.h"
-#include "path.h"
-#include "uri.h"
-#include "input_stream.h"
-
-#include <sys/types.h>
-#include <dirent.h>
-
-static void
-dirvec_print(struct client *client, const struct dirvec *dv)
-{
- size_t i;
-
- for (i = 0; i < dv->nr; ++i)
- client_printf(client, DIRECTORY_DIR "%s\n",
- directory_get_path(dv->base[i]));
-}
-
-static void
-print_playlist_in_directory(struct client *client,
- const struct directory *directory,
- const char *name_utf8)
-{
- if (directory_is_root(directory))
- client_printf(client, "playlist: %s\n", name_utf8);
- else
- client_printf(client, "playlist: %s/%s\n",
- directory_get_path(directory), name_utf8);
-}
-
-/**
- * Print a list of playlists in the specified directory.
- */
-static void
-directory_print_playlists(struct client *client,
- const struct directory *directory)
-{
- for (const struct playlist_metadata *pm = directory->playlists.head;
- pm != NULL; pm = pm->next)
- print_playlist_in_directory(client, directory, pm->name);
-}
-
-void
-directory_print(struct client *client, const struct directory *directory)
-{
- dirvec_print(client, &directory->children);
- songvec_print(client, &directory->songs);
- directory_print_playlists(client, directory);
-}
diff --git a/src/directory_save.c b/src/directory_save.c
index 55896c289..912e71e0e 100644
--- a/src/directory_save.c
+++ b/src/directory_save.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -42,9 +42,8 @@ directory_quark(void)
}
void
-directory_save(FILE *fp, struct directory *directory)
+directory_save(FILE *fp, const struct directory *directory)
{
- struct dirvec *children = &directory->children;
size_t i;
if (!directory_is_root(directory)) {
@@ -55,8 +54,9 @@ directory_save(FILE *fp, struct directory *directory)
directory_get_path(directory));
}
+ const struct dirvec *children = &directory->children;
for (i = 0; i < children->nr; ++i) {
- struct directory *cur = children->base[i];
+ const struct directory *cur = children->base[i];
char *base = g_path_get_basename(cur->path);
fprintf(fp, DIRECTORY_DIR "%s\n", base);
diff --git a/src/directory_save.h b/src/directory_save.h
index 9193b6c41..2d0056830 100644
--- a/src/directory_save.h
+++ b/src/directory_save.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -28,7 +28,7 @@
struct directory;
void
-directory_save(FILE *fp, struct directory *directory);
+directory_save(FILE *fp, const struct directory *directory);
bool
directory_load(FILE *fp, struct directory *directory,
diff --git a/src/dirvec.c b/src/dirvec.c
index 89b32a4f4..3df08e9ab 100644
--- a/src/dirvec.c
+++ b/src/dirvec.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/dirvec.h b/src/dirvec.h
index a1a97d9f1..a15510ab2 100644
--- a/src/dirvec.h
+++ b/src/dirvec.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder/flac_encoder.c b/src/encoder/flac_encoder.c
index c34faad00..8b315d710 100644
--- a/src/encoder/flac_encoder.c
+++ b/src/encoder/flac_encoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -205,7 +205,7 @@ flac_encoder_open(struct encoder *_encoder, struct audio_format *audio_format,
pcm_buffer_init(&encoder->buffer);
pcm_buffer_init(&encoder->expand_buffer);
- /* this immediatelly outputs data throught callback */
+ /* this immediately outputs data through callback */
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
{
diff --git a/src/encoder/lame_encoder.c b/src/encoder/lame_encoder.c
index df843471b..3bb99ea28 100644
--- a/src/encoder/lame_encoder.c
+++ b/src/encoder/lame_encoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder/null_encoder.c b/src/encoder/null_encoder.c
index bf7e61c3b..e83eaf2ba 100644
--- a/src/encoder/null_encoder.c
+++ b/src/encoder/null_encoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder/twolame_encoder.c b/src/encoder/twolame_encoder.c
index d20af551b..00ebcc0d7 100644
--- a/src/encoder/twolame_encoder.c
+++ b/src/encoder/twolame_encoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder/vorbis_encoder.c b/src/encoder/vorbis_encoder.c
index 38a998bd2..3e9d486b6 100644
--- a/src/encoder/vorbis_encoder.c
+++ b/src/encoder/vorbis_encoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder/wave_encoder.c b/src/encoder/wave_encoder.c
index 938be5e5e..6ebacab7d 100644
--- a/src/encoder/wave_encoder.c
+++ b/src/encoder/wave_encoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder_api.h b/src/encoder_api.h
index 5df486ebd..46c8d10c8 100644
--- a/src/encoder_api.h
+++ b/src/encoder_api.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder_list.c b/src/encoder_list.c
index f49ad48f7..d98e617b4 100644
--- a/src/encoder_list.c
+++ b/src/encoder_list.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder_list.h b/src/encoder_list.h
index 95f853004..6316d5d2f 100644
--- a/src/encoder_list.h
+++ b/src/encoder_list.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/encoder_plugin.h b/src/encoder_plugin.h
index fb00413e6..95b3da016 100644
--- a/src/encoder_plugin.h
+++ b/src/encoder_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -80,7 +80,7 @@ encoder_struct_init(struct encoder *encoder,
*
* @param plugin the encoder plugin
* @param param optional configuration
- * @param error location to store the error occuring, or NULL to ignore errors.
+ * @param error location to store the error occurring, or NULL to ignore errors.
* @return an encoder object on success, NULL on failure
*/
static inline struct encoder *
@@ -109,7 +109,7 @@ encoder_finish(struct encoder *encoder)
* @param encoder the encoder
* @param audio_format the encoder's input audio format; the plugin
* may modify the struct to adapt it to its abilities
- * @param error location to store the error occuring, or NULL to ignore errors.
+ * @param error location to store the error occurring, or NULL to ignore errors.
* @return true on success
*/
static inline bool
@@ -137,7 +137,7 @@ encoder_close(struct encoder *encoder)
* buffered available by encoder_read().
*
* @param encoder the encoder
- * @param error location to store the error occuring, or NULL to ignore errors.
+ * @param error location to store the error occurring, or NULL to ignore errors.
* @return true on success
*/
static inline bool
@@ -176,7 +176,7 @@ encoder_pre_tag(struct encoder *encoder, GError **error)
*
* @param encoder the encoder
* @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 occurring, or NULL to ignore errors.
* @return true on success
*/
static inline bool
@@ -194,7 +194,7 @@ encoder_tag(struct encoder *encoder, const struct tag *tag, GError **error)
* @param encoder the encoder
* @param data the buffer containing PCM samples
* @param length the length of the buffer in bytes
- * @param error location to store the error occuring, or NULL to ignore errors.
+ * @param error location to store the error occurring, or NULL to ignore errors.
* @return true on success
*/
static inline bool
diff --git a/src/event_pipe.c b/src/event_pipe.c
index 5b519984f..85fd6d917 100644
--- a/src/event_pipe.c
+++ b/src/event_pipe.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/event_pipe.h b/src/event_pipe.h
index 923544bf4..3734bb86c 100644
--- a/src/event_pipe.h
+++ b/src/event_pipe.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/exclude.c b/src/exclude.c
index dd46b58c7..438039d30 100644
--- a/src/exclude.c
+++ b/src/exclude.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/exclude.h b/src/exclude.h
index fd7cf8795..5b1229e29 100644
--- a/src/exclude.h
+++ b/src/exclude.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/fd_util.c b/src/fd_util.c
index 1f3004d0c..36f6e2fc9 100644
--- a/src/fd_util.c
+++ b/src/fd_util.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
@@ -206,6 +206,29 @@ socketpair_cloexec(int domain, int type, int protocol, int sv[2])
return ret;
}
+int
+socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2])
+{
+ int ret;
+
+#if defined(SOCK_CLOEXEC) && defined(SOCK_NONBLOCK)
+ ret = socketpair(domain, type | SOCK_CLOEXEC | SOCK_NONBLOCK, protocol,
+ sv);
+ if (ret >= 0 || errno != EINVAL)
+ return ret;
+#endif
+
+ ret = socketpair(domain, type, protocol, sv);
+ if (ret >= 0) {
+ fd_set_cloexec(sv[0], true);
+ fd_set_nonblock(sv[0]);
+ fd_set_cloexec(sv[1], true);
+ fd_set_nonblock(sv[1]);
+ }
+
+ return ret;
+}
+
#endif
int
diff --git a/src/fd_util.h b/src/fd_util.h
index 5b80df2c7..96ad784d4 100644
--- a/src/fd_util.h
+++ b/src/fd_util.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
@@ -89,6 +89,13 @@ pipe_cloexec_nonblock(int fd[2]);
int
socketpair_cloexec(int domain, int type, int protocol, int sv[2]);
+/**
+ * Wrapper for socketpair(), which sets the flags CLOEXEC and NONBLOCK
+ * (atomically if supported by the OS).
+ */
+int
+socketpair_cloexec_nonblock(int domain, int type, int protocol, int sv[2]);
+
#endif
/**
diff --git a/src/fifo_buffer.c b/src/fifo_buffer.c
index 9ac7270bb..e5735f5eb 100644
--- a/src/fifo_buffer.c
+++ b/src/fifo_buffer.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
diff --git a/src/fifo_buffer.h b/src/fifo_buffer.h
index 661dfd57e..2730207b5 100644
--- a/src/fifo_buffer.h
+++ b/src/fifo_buffer.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
diff --git a/src/filter/autoconvert_filter_plugin.c b/src/filter/autoconvert_filter_plugin.c
index 9e197a5f6..3826a0fb3 100644
--- a/src/filter/autoconvert_filter_plugin.c
+++ b/src/filter/autoconvert_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/autoconvert_filter_plugin.h b/src/filter/autoconvert_filter_plugin.h
index 730db197d..def08ab7e 100644
--- a/src/filter/autoconvert_filter_plugin.h
+++ b/src/filter/autoconvert_filter_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/chain_filter_plugin.c b/src/filter/chain_filter_plugin.c
index 06d4d0e6b..2c785a36f 100644
--- a/src/filter/chain_filter_plugin.c
+++ b/src/filter/chain_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/chain_filter_plugin.h b/src/filter/chain_filter_plugin.h
index 42c6a9b78..1dba46667 100644
--- a/src/filter/chain_filter_plugin.h
+++ b/src/filter/chain_filter_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/convert_filter_plugin.c b/src/filter/convert_filter_plugin.c
index cb9e0940a..757084de1 100644
--- a/src/filter/convert_filter_plugin.c
+++ b/src/filter/convert_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/convert_filter_plugin.h b/src/filter/convert_filter_plugin.h
index ba9180e64..156adf8e3 100644
--- a/src/filter/convert_filter_plugin.h
+++ b/src/filter/convert_filter_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/normalize_filter_plugin.c b/src/filter/normalize_filter_plugin.c
index 63bbb6e4f..fa992f0d4 100644
--- a/src/filter/normalize_filter_plugin.c
+++ b/src/filter/normalize_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/null_filter_plugin.c b/src/filter/null_filter_plugin.c
index 650f95bc4..e7c998827 100644
--- a/src/filter/null_filter_plugin.c
+++ b/src/filter/null_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/replay_gain_filter_plugin.c b/src/filter/replay_gain_filter_plugin.c
index 3a0af66ff..656e464e2 100644
--- a/src/filter/replay_gain_filter_plugin.c
+++ b/src/filter/replay_gain_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/replay_gain_filter_plugin.h b/src/filter/replay_gain_filter_plugin.h
index 348b4f50c..45b738e40 100644
--- a/src/filter/replay_gain_filter_plugin.h
+++ b/src/filter/replay_gain_filter_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/route_filter_plugin.c b/src/filter/route_filter_plugin.c
index 6b9aa2a2f..3bf8677e5 100644
--- a/src/filter/route_filter_plugin.c
+++ b/src/filter/route_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/volume_filter_plugin.c b/src/filter/volume_filter_plugin.c
index 42311ca5e..8c50e3cd1 100644
--- a/src/filter/volume_filter_plugin.c
+++ b/src/filter/volume_filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter/volume_filter_plugin.h b/src/filter/volume_filter_plugin.h
index ad3b2c6f1..5b16f7e57 100644
--- a/src/filter/volume_filter_plugin.h
+++ b/src/filter/volume_filter_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter_config.c b/src/filter_config.c
index 90de199b7..ab9bdb0c5 100644
--- a/src/filter_config.c
+++ b/src/filter_config.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter_config.h b/src/filter_config.h
index 9ed4d204b..920cbc07c 100644
--- a/src/filter_config.h
+++ b/src/filter_config.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter_internal.h b/src/filter_internal.h
index 8dd6da491..4e94599a2 100644
--- a/src/filter_internal.h
+++ b/src/filter_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter_plugin.c b/src/filter_plugin.c
index 492d703ac..7173134b3 100644
--- a/src/filter_plugin.c
+++ b/src/filter_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter_plugin.h b/src/filter_plugin.h
index ac6b34522..58e34dfb2 100644
--- a/src/filter_plugin.h
+++ b/src/filter_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -79,7 +79,7 @@ struct filter_plugin {
*
* @param plugin the filter plugin
* @param param optional configuration section
- * @param error location to store the error occuring, or NULL to
+ * @param error location to store the error occurring, or NULL to
* ignore errors.
* @return a new filter object, or NULL on error
*/
@@ -92,7 +92,7 @@ filter_new(const struct filter_plugin *plugin,
* the specified configuration section.
*
* @param param the configuration section
- * @param error location to store the error occuring, or NULL to
+ * @param error location to store the error occurring, or NULL to
* ignore errors.
* @return a new filter object, or NULL on error
*/
@@ -114,7 +114,7 @@ filter_free(struct filter *filter);
* @param filter the filter object
* @param audio_format the audio format of incoming data; the plugin
* may modify the object to enforce another input format
- * @param error location to store the error occuring, or NULL to
+ * @param error location to store the error occurring, or NULL to
* ignore errors.
* @return the format of outgoing data
*/
@@ -137,7 +137,7 @@ filter_close(struct filter *filter);
* @param src the input buffer
* @param src_size the size of #src_buffer in bytes
* @param dest_size_r the size of the returned buffer
- * @param error location to store the error occuring, or NULL to
+ * @param error location to store the error occurring, or NULL to
* ignore errors.
* @return the destination buffer on success (will be invalidated by
* filter_close() or filter_filter()), NULL on error
diff --git a/src/filter_registry.c b/src/filter_registry.c
index 150043cc5..dc1889398 100644
--- a/src/filter_registry.c
+++ b/src/filter_registry.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/filter_registry.h b/src/filter_registry.h
index 551a7afa1..d3949c7c4 100644
--- a/src/filter_registry.h
+++ b/src/filter_registry.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/gcc.h b/src/gcc.h
index 085a8a5f6..45f7101f3 100644
--- a/src/gcc.h
+++ b/src/gcc.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,35 +20,44 @@
#ifndef MPD_GCC_H
#define MPD_GCC_H
+#define GCC_CHECK_VERSION(major, minor) \
+ (defined(__GNUC__) && \
+ (__GNUC__ > (major) || \
+ (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor))))
+
/* this allows us to take advantage of special gcc features while still
* allowing other compilers to compile:
*
* example taken from: http://rlove.org/log/2005102601
*/
-#if defined(__GNUC__) && (__GNUC__ >= 3)
-# define mpd_must_check __attribute__ ((warn_unused_result))
-# define mpd_packed __attribute__ ((packed))
+#if GCC_CHECK_VERSION(3,0)
+# define gcc_must_check __attribute__ ((warn_unused_result))
+# define gcc_packed __attribute__ ((packed))
/* these are very useful for type checking */
-# define mpd_printf __attribute__ ((format(printf,1,2)))
-# define mpd_fprintf __attribute__ ((format(printf,2,3)))
-# define mpd_fprintf_ __attribute__ ((format(printf,3,4)))
-# define mpd_fprintf__ __attribute__ ((format(printf,4,5)))
-# define mpd_scanf __attribute__ ((format(scanf,1,2)))
-# define mpd_used __attribute__ ((used))
+# define gcc_printf __attribute__ ((format(printf,1,2)))
+# define gcc_fprintf __attribute__ ((format(printf,2,3)))
+# define gcc_fprintf_ __attribute__ ((format(printf,3,4)))
+# define gcc_fprintf__ __attribute__ ((format(printf,4,5)))
+# define gcc_scanf __attribute__ ((format(scanf,1,2)))
+# define gcc_used __attribute__ ((used))
/* # define inline inline __attribute__ ((always_inline)) */
-# define mpd_noinline __attribute__ ((noinline))
+# define gcc_noinline __attribute__ ((noinline))
+# define gcc_nonnull(...) __attribute__((nonnull(__VA_ARGS__)))
+# define gcc_nonnull_all __attribute__((nonnull))
#else
-# define mpd_must_check
-# define mpd_packed
-# define mpd_printf
-# define mpd_fprintf
-# define mpd_fprintf_
-# define mpd_fprintf__
-# define mpd_scanf
-# define mpd_used
+# define gcc_must_check
+# define gcc_packed
+# define gcc_printf
+# define gcc_fprintf
+# define gcc_fprintf_
+# define gcc_fprintf__
+# define gcc_scanf
+# define gcc_used
/* # define inline */
-# define mpd_noinline
+# define gcc_noinline
+# define gcc_nonnull(...)
+# define gcc_nonnull_all
#endif
#endif /* MPD_GCC_H */
diff --git a/src/glib_compat.h b/src/glib_compat.h
index 4d0e7040d..f35576fa3 100644
--- a/src/glib_compat.h
+++ b/src/glib_compat.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -32,6 +32,12 @@
#define g_queue_clear(q) do { g_queue_free(q); q = g_queue_new(); } while (0)
+static inline GSource *
+g_timeout_source_new_seconds(guint interval)
+{
+ return g_timeout_source_new(interval * 1000);
+}
+
static inline guint
g_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data)
{
@@ -43,6 +49,12 @@ g_timeout_add_seconds(guint interval, GSourceFunc function, gpointer data)
#if !GLIB_CHECK_VERSION(2,16,0)
static inline void
+g_prefix_error(G_GNUC_UNUSED GError **error_r,
+ G_GNUC_UNUSED const gchar *format, ...)
+{
+}
+
+static inline void
g_propagate_prefixed_error(GError **dest_r, GError *src,
G_GNUC_UNUSED const gchar *format, ...)
{
@@ -74,4 +86,15 @@ g_uri_parse_scheme(const char *uri)
#endif
+#if !GLIB_CHECK_VERSION(2,18,0)
+
+static inline void
+g_set_error_literal(GError **err, GQuark domain, gint code,
+ const gchar *message)
+{
+ g_set_error(err, domain, code, "%s", message);
+}
+
+#endif
+
#endif
diff --git a/src/icy_metadata.c b/src/icy_metadata.c
index 6a79121cf..32953e69f 100644
--- a/src/icy_metadata.c
+++ b/src/icy_metadata.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/icy_metadata.h b/src/icy_metadata.h
index 4a51b4cf0..9797122ca 100644
--- a/src/icy_metadata.h
+++ b/src/icy_metadata.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/icy_server.c b/src/icy_server.c
index 62a2c67af..b6c89eaf6 100644
--- a/src/icy_server.c
+++ b/src/icy_server.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -70,7 +70,7 @@ icy_server_metadata_string(const char *stream_title, const char* stream_url)
meta_length = strlen(icy_metadata);
- meta_length--; // substract placeholder
+ meta_length--; // subtract placeholder
meta_length = ((int)meta_length / 16) + 1;
diff --git a/src/icy_server.h b/src/icy_server.h
index 3ce4ab635..04f21d2ad 100644
--- a/src/icy_server.h
+++ b/src/icy_server.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/idle.c b/src/idle.c
index eccb62322..2d174d78a 100644
--- a/src/idle.c
+++ b/src/idle.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -42,6 +42,8 @@ static const char *const idle_names[] = {
"options",
"sticker",
"update",
+ "subscription",
+ "message",
NULL
};
diff --git a/src/idle.h b/src/idle.h
index 7caeb4a8c..0156933c0 100644
--- a/src/idle.h
+++ b/src/idle.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -53,6 +53,12 @@ enum {
/** a database update has started or finished. */
IDLE_UPDATE = 0x100,
+
+ /** a client has subscribed or unsubscribed to/from a channel */
+ IDLE_SUBSCRIPTION = 0x200,
+
+ /** a message on the subscribed channel was receivedd */
+ IDLE_MESSAGE = 0x400,
};
/**
diff --git a/src/inotify_queue.c b/src/inotify_queue.c
index 5391a1715..d5e2228c3 100644
--- a/src/inotify_queue.c
+++ b/src/inotify_queue.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/inotify_queue.h b/src/inotify_queue.h
index 2e43d2f25..cfc28ebfe 100644
--- a/src/inotify_queue.h
+++ b/src/inotify_queue.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/inotify_source.c b/src/inotify_source.c
index 3a986cbad..e415f5e72 100644
--- a/src/inotify_source.c
+++ b/src/inotify_source.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/inotify_source.h b/src/inotify_source.h
index e78b92c0f..f92e18e39 100644
--- a/src/inotify_source.h
+++ b/src/inotify_source.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/inotify_update.c b/src/inotify_update.c
index 8d9657961..e78c15fc1 100644
--- a/src/inotify_update.c
+++ b/src/inotify_update.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/inotify_update.h b/src/inotify_update.h
index 92b4e0cc6..ca75c0f45 100644
--- a/src/inotify_update.h
+++ b/src/inotify_update.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input/archive_input_plugin.c b/src/input/archive_input_plugin.c
index 97e4836ff..8d78f4c89 100644
--- a/src/input/archive_input_plugin.c
+++ b/src/input/archive_input_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input/archive_input_plugin.h b/src/input/archive_input_plugin.h
index 20568cfbe..51095f37f 100644
--- a/src/input/archive_input_plugin.h
+++ b/src/input/archive_input_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input/cdio_paranoia_input_plugin.c b/src/input/cdio_paranoia_input_plugin.c
new file mode 100644
index 000000000..da2ed1096
--- /dev/null
+++ b/src/input/cdio_paranoia_input_plugin.c
@@ -0,0 +1,389 @@
+/*
+ * 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.
+ */
+
+/**
+ * CD-Audio handling (requires libcdio_paranoia)
+ */
+
+#include "config.h"
+#include "input/cdio_paranoia_input_plugin.h"
+#include "input_internal.h"
+#include "input_plugin.h"
+#include "refcount.h"
+#include "pcm_buffer.h"
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <assert.h>
+
+#include <cdio/paranoia.h>
+#include <cdio/cd_types.h>
+
+struct input_cdio_paranoia {
+ struct input_stream base;
+
+ cdrom_drive_t *drv;
+ CdIo_t *cdio;
+ cdrom_paranoia_t *para;
+
+ int endian;
+
+ lsn_t lsn_from, lsn_to;
+ int lsn_relofs;
+
+ int trackno;
+
+ char buffer[CDIO_CD_FRAMESIZE_RAW];
+ int buffer_lsn;
+
+ struct pcm_buffer conv_buffer;
+};
+
+static inline GQuark
+cdio_quark(void)
+{
+ return g_quark_from_static_string("cdio");
+}
+
+static void
+input_cdio_close(struct input_stream *is)
+{
+ struct input_cdio_paranoia *i = (struct input_cdio_paranoia *)is;
+
+ pcm_buffer_deinit(&i->conv_buffer);
+
+ if (i->para)
+ cdio_paranoia_free(i->para);
+ if (i->drv)
+ cdio_cddap_close_no_free_cdio( i->drv);
+ if (i->cdio)
+ cdio_destroy( i->cdio );
+
+ input_stream_deinit(&i->base);
+ g_free(i);
+}
+
+struct cdio_uri {
+ char device[64];
+ int track;
+};
+
+static bool
+parse_cdio_uri(struct cdio_uri *dest, const char *src, GError **error_r)
+{
+ if (!g_str_has_prefix(src, "cdda://"))
+ return false;
+
+ src += 7;
+
+ if (*src == 0) {
+ /* play the whole CD in the default drive */
+ dest->device[0] = 0;
+ dest->track = -1;
+ return true;
+ }
+
+ const char *slash = strrchr(src, '/');
+ if (slash == NULL) {
+ /* play the whole CD in the specified drive */
+ g_strlcpy(dest->device, src, sizeof(dest->device));
+ dest->track = -1;
+ return true;
+ }
+
+ size_t device_length = slash - src;
+ if (device_length >= sizeof(dest->device))
+ device_length = sizeof(dest->device) - 1;
+
+ memcpy(dest->device, src, device_length);
+ dest->device[device_length] = 0;
+
+ const char *track = slash + 1;
+
+ char *endptr;
+ dest->track = strtoul(track, &endptr, 10);
+ if (*endptr != 0) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Malformed track number");
+ return false;
+ }
+
+ if (endptr == track)
+ /* play the whole CD */
+ dest->track = -1;
+
+ return true;
+}
+
+static char *
+cdio_detect_device(void)
+{
+ char **devices = cdio_get_devices_with_cap(NULL, CDIO_FS_AUDIO, false);
+ if (devices == NULL)
+ return NULL;
+
+ char *device = g_strdup(devices[0]);
+ cdio_free_device_list(devices);
+
+ return device;
+}
+
+static struct input_stream *
+input_cdio_open(const char *uri, GError **error_r)
+{
+ struct input_cdio_paranoia *i;
+
+ struct cdio_uri parsed_uri;
+ if (!parse_cdio_uri(&parsed_uri, uri, error_r))
+ return NULL;
+
+ i = g_new(struct input_cdio_paranoia, 1);
+ input_stream_init(&i->base, &input_plugin_cdio_paranoia, uri);
+
+ /* initialize everything (should be already) */
+ i->drv = NULL;
+ i->cdio = NULL;
+ i->para = NULL;
+ i->trackno = parsed_uri.track;
+ pcm_buffer_init(&i->conv_buffer);
+
+ /* get list of CD's supporting CD-DA */
+ char *device = parsed_uri.device[0] != 0
+ ? g_strdup(parsed_uri.device)
+ : cdio_detect_device();
+ if (device == NULL) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Unable find or access a CD-ROM drive with an audio CD in it.");
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ /* Found such a CD-ROM with a CD-DA loaded. Use the first drive in the list. */
+ i->cdio = cdio_open(device, DRIVER_UNKNOWN);
+ g_free(device);
+
+ i->drv = cdio_cddap_identify_cdio(i->cdio, 1, NULL);
+
+ if ( !i->drv ) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Unable to identify audio CD disc.");
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ cdda_verbose_set(i->drv, CDDA_MESSAGE_FORGETIT, CDDA_MESSAGE_FORGETIT);
+
+ if ( 0 != cdio_cddap_open(i->drv) ) {
+ g_set_error(error_r, cdio_quark(), 0, "Unable to open disc.");
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ i->endian = data_bigendianp(i->drv);
+ switch (i->endian) {
+ case -1:
+ g_debug("cdda: drive returns unknown audio data, assuming Little Endian");
+ i->endian = 0;
+ break;
+ case 0:
+ g_debug("cdda: drive returns audio data Little Endian.");
+ break;
+ case 1:
+ g_debug("cdda: drive returns audio data Big Endian.");
+ break;
+ default:
+ g_set_error(error_r, cdio_quark(), 0,
+ "Drive returns unknown data type %d", i->endian);
+ input_cdio_close(&i->base);
+ return NULL;
+ }
+
+ i->lsn_relofs = 0;
+
+ if (i->trackno >= 0) {
+ i->lsn_from = cdio_get_track_lsn(i->cdio, i->trackno);
+ i->lsn_to = cdio_get_track_last_lsn(i->cdio, i->trackno);
+ } else {
+ i->lsn_from = 0;
+ i->lsn_to = cdio_get_disc_last_lsn(i->cdio);
+ }
+
+ i->para = cdio_paranoia_init(i->drv);
+
+ /* Set reading mode for full paranoia, but allow skipping sectors. */
+ paranoia_modeset(i->para, PARANOIA_MODE_FULL^PARANOIA_MODE_NEVERSKIP);
+
+ /* seek to beginning of the track */
+ cdio_paranoia_seek(i->para, i->lsn_from, SEEK_SET);
+
+ i->base.ready = true;
+ i->base.seekable = true;
+ i->base.size = (i->lsn_to - i->lsn_from + 1) * CDIO_CD_FRAMESIZE_RAW;
+
+ /* hack to make MPD select the "pcm" decoder plugin */
+ i->base.mime = g_strdup("audio/x-mpd-cdda-pcm");
+
+ return &i->base;
+}
+
+static bool
+input_cdio_seek(struct input_stream *is,
+ goffset offset, int whence, GError **error_r)
+{
+ struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+
+ /* calculate absolute offset */
+ switch (whence) {
+ case SEEK_SET:
+ break;
+ case SEEK_CUR:
+ offset += cis->base.offset;
+ break;
+ case SEEK_END:
+ offset += cis->base.size;
+ break;
+ }
+
+ if (offset < 0 || offset > cis->base.size) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "Invalid offset to seek %ld (%ld)",
+ (long int)offset, (long int)cis->base.size);
+ return false;
+ }
+
+ /* simple case */
+ if (offset == cis->base.offset)
+ return true;
+
+ /* calculate current LSN */
+ cis->lsn_relofs = offset / CDIO_CD_FRAMESIZE_RAW;
+ cis->base.offset = offset;
+
+ cdio_paranoia_seek(cis->para, cis->lsn_from + cis->lsn_relofs, SEEK_SET);
+
+ return true;
+}
+
+static inline size_t
+pcm16_to_wave(uint16_t *dst16, const uint16_t *src16, size_t length)
+{
+ size_t cnt = length >> 1;
+ while (cnt > 0) {
+ *dst16++ = GUINT16_TO_LE(*src16++);
+ cnt--;
+ }
+ return length;
+}
+
+static size_t
+input_cdio_read(struct input_stream *is, void *ptr, size_t length,
+ GError **error_r)
+{
+ struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+ size_t nbytes = 0;
+ int diff;
+ size_t len, maxwrite;
+ int16_t *rbuf;
+ char *s_err, *s_mess;
+ char *wptr = (char *) ptr;
+
+ while (length > 0) {
+
+
+ /* end of track ? */
+ if (cis->lsn_from + cis->lsn_relofs > cis->lsn_to)
+ break;
+
+ //current sector was changed ?
+ if (cis->lsn_relofs != cis->buffer_lsn) {
+ rbuf = cdio_paranoia_read(cis->para, NULL);
+
+ s_err = cdda_errors(cis->drv);
+ if (s_err) {
+ g_warning("paranoia_read: %s", s_err );
+ free(s_err);
+ }
+ s_mess = cdda_messages(cis->drv);
+ if (s_mess) {
+ free(s_mess);
+ }
+ if (!rbuf) {
+ g_set_error(error_r, cdio_quark(), 0,
+ "paranoia read error. Stopping.");
+ return 0;
+ }
+ //do the swapping if nessesary
+ if (cis->endian != 0) {
+ uint16_t *conv_buffer = pcm_buffer_get(&cis->conv_buffer, CDIO_CD_FRAMESIZE_RAW );
+ /* do endian conversion ! */
+ pcm16_to_wave( conv_buffer, (uint16_t*) rbuf, CDIO_CD_FRAMESIZE_RAW);
+ rbuf = (int16_t *)conv_buffer;
+ }
+ //store current buffer
+ memcpy(cis->buffer, rbuf, CDIO_CD_FRAMESIZE_RAW);
+ cis->buffer_lsn = cis->lsn_relofs;
+ } else {
+ //use cached sector
+ rbuf = (int16_t*) cis->buffer;
+ }
+
+ //correct offset
+ diff = cis->base.offset - cis->lsn_relofs * CDIO_CD_FRAMESIZE_RAW;
+
+ assert(diff >= 0 && diff < CDIO_CD_FRAMESIZE_RAW);
+
+ maxwrite = CDIO_CD_FRAMESIZE_RAW - diff; //# of bytes pending in current buffer
+ len = (length < maxwrite? length : maxwrite);
+
+ //skip diff bytes from this lsn
+ memcpy(wptr, ((char*)rbuf) + diff, len);
+ //update pointer
+ wptr += len;
+ nbytes += len;
+
+ //update offset
+ cis->base.offset += len;
+ cis->lsn_relofs = cis->base.offset / CDIO_CD_FRAMESIZE_RAW;
+ //update length
+ length -= len;
+ }
+
+ return nbytes;
+}
+
+static bool
+input_cdio_eof(struct input_stream *is)
+{
+ struct input_cdio_paranoia *cis = (struct input_cdio_paranoia *)is;
+
+ return (cis->lsn_from + cis->lsn_relofs > cis->lsn_to);
+}
+
+const struct input_plugin input_plugin_cdio_paranoia = {
+ .name = "cdio_paranoia",
+ .open = input_cdio_open,
+ .close = input_cdio_close,
+ .seek = input_cdio_seek,
+ .read = input_cdio_read,
+ .eof = input_cdio_eof
+};
diff --git a/src/input/cdio_paranoia_input_plugin.h b/src/input/cdio_paranoia_input_plugin.h
new file mode 100644
index 000000000..71c5cbe8d
--- /dev/null
+++ b/src/input/cdio_paranoia_input_plugin.h
@@ -0,0 +1,28 @@
+/*
+ * 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_CDIO_PARANOIA_INPUT_PLUGIN_H
+#define MPD_CDIO_PARANOIA_INPUT_PLUGIN_H
+
+/**
+ * An input plugin based on libcdio_paranoia library.
+ */
+extern const struct input_plugin input_plugin_cdio_paranoia;
+
+#endif
diff --git a/src/input/curl_input_plugin.c b/src/input/curl_input_plugin.c
index 604965dd1..dfe2e53c5 100644
--- a/src/input/curl_input_plugin.c
+++ b/src/input/curl_input_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,10 +19,12 @@
#include "config.h"
#include "input/curl_input_plugin.h"
+#include "input_internal.h"
#include "input_plugin.h"
#include "conf.h"
#include "tag.h"
#include "icy_metadata.h"
+#include "io_thread.h"
#include "glib_compat.h"
#include <assert.h>
@@ -73,17 +75,32 @@ struct input_curl {
/** the curl handles */
CURL *easy;
- CURLM *multi;
+
+ GMutex *mutex;
+ GCond *cond;
+
+ /** the GMainLoop source used to poll all CURL file
+ descriptors */
+ GSource *source;
+
+ /** the source id of #source */
+ guint source_id;
+
+ /** a linked list of all registered GPollFD objects */
+ GSList *fds;
/** list of buffers, where input_curl_writefunction() appends
to, and input_curl_read() reads from them */
GQueue *buffers;
- /** has something been added to the buffers list? */
- bool buffered;
-
- /** did libcurl tell us the we're at the end of the response body? */
- bool eof;
+#if LIBCURL_VERSION_NUM >= 0x071200
+ /**
+ * Is the connection currently paused? That happens when the
+ * buffer was getting too large. It will be unpaused when the
+ * buffer is below the threshold again.
+ */
+ bool paused;
+#endif
/** error message provided by libcurl */
char error[CURL_ERROR_SIZE];
@@ -97,6 +114,8 @@ struct input_curl {
/** the tag object ready to be requested via
input_stream_tag() */
struct tag *tag;
+
+ GError *postponed_error;
};
/** libcurl should accept "ICY 200 OK" */
@@ -106,20 +125,564 @@ static struct curl_slist *http_200_aliases;
static const char *proxy, *proxy_user, *proxy_password;
static unsigned proxy_port;
+static struct {
+ CURLM *multi;
+
+ /**
+ * A linked list of all active HTTP requests. An active
+ * request is one that doesn't have the "eof" flag set.
+ */
+ GSList *requests;
+
+ /**
+ * The GMainLoop source used to poll all CURL file
+ * descriptors.
+ */
+ GSource *source;
+
+ /**
+ * The source id of #source.
+ */
+ guint source_id;
+
+ GSList *fds;
+
+ /**
+ * When this is non-zero, then an update of #fds is scheduled.
+ */
+ guint dirty_source_id;
+
+#if LIBCURL_VERSION_NUM >= 0x070f04
+ /**
+ * Did CURL give us a timeout? If yes, then we need to call
+ * curl_multi_perform(), even if there was no event on any
+ * file descriptor.
+ */
+ bool timeout;
+
+ /**
+ * The absolute time stamp when the timeout expires. This is
+ * used in the GSource method check().
+ */
+ GTimeVal absolute_timeout;
+#endif
+} curl;
+
static inline GQuark
curl_quark(void)
{
return g_quark_from_static_string("curl");
}
+/**
+ * Find a request by its CURL "easy" handle.
+ *
+ * Runs in the I/O thread. No lock needed.
+ */
+static struct input_curl *
+input_curl_find_request(CURL *easy)
+{
+ assert(io_thread_inside());
+
+ for (GSList *i = curl.requests; i != NULL; i = g_slist_next(i)) {
+ struct input_curl *c = i->data;
+ if (c->easy == easy)
+ return c;
+ }
+
+ return NULL;
+}
+
+#if LIBCURL_VERSION_NUM >= 0x071200
+
+static gpointer
+input_curl_resume(gpointer data)
+{
+ assert(io_thread_inside());
+
+ struct input_curl *c = data;
+
+ if (c->paused) {
+ curl_easy_pause(c->easy, CURLPAUSE_CONT);
+ c->paused = false;
+ }
+
+ return NULL;
+}
+
+#endif
+
+/**
+ * Calculates the GLib event bit mask for one file descriptor,
+ * obtained from three #fd_set objects filled by curl_multi_fdset().
+ */
+static gushort
+input_curl_fd_events(int fd, fd_set *rfds, fd_set *wfds, fd_set *efds)
+{
+ gushort events = 0;
+
+ if (FD_ISSET(fd, rfds)) {
+ events |= G_IO_IN | G_IO_HUP | G_IO_ERR;
+ FD_CLR(fd, rfds);
+ }
+
+ if (FD_ISSET(fd, wfds)) {
+ events |= G_IO_OUT | G_IO_ERR;
+ FD_CLR(fd, wfds);
+ }
+
+ if (FD_ISSET(fd, efds)) {
+ events |= G_IO_HUP | G_IO_ERR;
+ FD_CLR(fd, efds);
+ }
+
+ return events;
+}
+
+/**
+ * Updates all registered GPollFD objects, unregisters old ones,
+ * registers new ones.
+ *
+ * Runs in the I/O thread. No lock needed.
+ */
+static void
+curl_update_fds(void)
+{
+ assert(io_thread_inside());
+
+ fd_set rfds, wfds, efds;
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+
+ int max_fd;
+ CURLMcode mcode = curl_multi_fdset(curl.multi, &rfds, &wfds,
+ &efds, &max_fd);
+ if (mcode != CURLM_OK) {
+ g_warning("curl_multi_fdset() failed: %s\n",
+ curl_multi_strerror(mcode));
+ return;
+ }
+
+ GSList *fds = curl.fds;
+ curl.fds = NULL;
+
+ while (fds != NULL) {
+ GPollFD *poll_fd = fds->data;
+ gushort events = input_curl_fd_events(poll_fd->fd, &rfds,
+ &wfds, &efds);
+
+ assert(poll_fd->events != 0);
+
+ fds = g_slist_remove(fds, poll_fd);
+
+ if (events != poll_fd->events)
+ g_source_remove_poll(curl.source, poll_fd);
+
+ if (events != 0) {
+ if (events != poll_fd->events) {
+ poll_fd->events = events;
+ g_source_add_poll(curl.source, poll_fd);
+ }
+
+ curl.fds = g_slist_prepend(curl.fds, poll_fd);
+ } else {
+ g_free(poll_fd);
+ }
+ }
+
+ for (int fd = 0; fd <= max_fd; ++fd) {
+ gushort events = input_curl_fd_events(fd, &rfds, &wfds, &efds);
+ if (events != 0) {
+ GPollFD *poll_fd = g_new(GPollFD, 1);
+ poll_fd->fd = fd;
+ poll_fd->events = events;
+ g_source_add_poll(curl.source, poll_fd);
+ curl.fds = g_slist_prepend(curl.fds, poll_fd);
+ }
+ }
+}
+
+/**
+ * Callback for curl_schedule_update() that runs in the I/O thread.
+ */
+static gboolean
+input_curl_dirty_callback(G_GNUC_UNUSED gpointer data)
+{
+ assert(io_thread_inside());
+ assert(curl.dirty_source_id != 0 || curl.requests == NULL);
+ curl.dirty_source_id = 0;
+
+ curl_update_fds();
+
+ return false;
+}
+
+/**
+ * Schedule a refresh of curl.fds. Does nothing if that is already
+ * scheduled.
+ *
+ * No lock needed.
+ */
+static void
+input_curl_schedule_update(void)
+{
+ if (curl.dirty_source_id != 0)
+ /* already scheduled */
+ return;
+
+ curl.dirty_source_id =
+ io_thread_idle_add(input_curl_dirty_callback, NULL);
+}
+
+/**
+ * Runs in the I/O thread. No lock needed.
+ */
+static bool
+input_curl_easy_add(struct input_curl *c, GError **error_r)
+{
+ assert(io_thread_inside());
+ assert(c != NULL);
+ assert(c->easy != NULL);
+ assert(input_curl_find_request(c->easy) == NULL);
+
+ curl.requests = g_slist_prepend(curl.requests, c);
+
+ CURLMcode mcode = curl_multi_add_handle(curl.multi, c->easy);
+ if (mcode != CURLM_OK) {
+ g_set_error(error_r, curl_quark(), mcode,
+ "curl_multi_add_handle() failed: %s",
+ curl_multi_strerror(mcode));
+ return false;
+ }
+
+ input_curl_schedule_update();
+
+ return true;
+}
+
+struct easy_add_params {
+ struct input_curl *c;
+ GError **error_r;
+};
+
+static gpointer
+input_curl_easy_add_callback(gpointer data)
+{
+ const struct easy_add_params *params = data;
+
+ bool success = input_curl_easy_add(params->c, params->error_r);
+ return GUINT_TO_POINTER(success);
+}
+
+/**
+ * Call input_curl_easy_add() in the I/O thread. May be called from
+ * any thread. Caller must not hold a mutex.
+ */
+static bool
+input_curl_easy_add_indirect(struct input_curl *c, GError **error_r)
+{
+ assert(c != NULL);
+ assert(c->easy != NULL);
+
+ struct easy_add_params params = {
+ .c = c,
+ .error_r = error_r,
+ };
+
+ gpointer result =
+ io_thread_call(input_curl_easy_add_callback, &params);
+ return GPOINTER_TO_UINT(result);
+}
+
+/**
+ * Frees the current "libcurl easy" handle, and everything associated
+ * with it.
+ *
+ * Runs in the I/O thread.
+ */
+static void
+input_curl_easy_free(struct input_curl *c)
+{
+ assert(io_thread_inside());
+ assert(c != NULL);
+
+ if (c->easy == NULL)
+ return;
+
+ curl.requests = g_slist_remove(curl.requests, c);
+
+ curl_multi_remove_handle(curl.multi, c->easy);
+ curl_easy_cleanup(c->easy);
+ c->easy = NULL;
+
+ curl_slist_free_all(c->request_headers);
+ c->request_headers = NULL;
+
+ g_free(c->range);
+ c->range = NULL;
+}
+
+static gpointer
+input_curl_easy_free_callback(gpointer data)
+{
+ struct input_curl *c = data;
+
+ input_curl_easy_free(c);
+ curl_update_fds();
+
+ return NULL;
+}
+
+/**
+ * Frees the current "libcurl easy" handle, and everything associated
+ * with it.
+ *
+ * The mutex must not be locked.
+ */
+static void
+input_curl_easy_free_indirect(struct input_curl *c)
+{
+ io_thread_call(input_curl_easy_free_callback, c);
+ assert(c->easy == NULL);
+}
+
+/**
+ * Abort and free all HTTP requests.
+ *
+ * Runs in the I/O thread. The caller must not hold locks.
+ */
+static void
+input_curl_abort_all_requests(GError *error)
+{
+ assert(io_thread_inside());
+ assert(error != NULL);
+
+ while (curl.requests != NULL) {
+ struct input_curl *c = curl.requests->data;
+ assert(c->postponed_error == NULL);
+
+ input_curl_easy_free(c);
+
+ g_mutex_lock(c->mutex);
+ c->postponed_error = g_error_copy(error);
+ c->base.ready = true;
+ g_cond_broadcast(c->cond);
+ g_mutex_unlock(c->mutex);
+ }
+
+ g_error_free(error);
+
+}
+
+/**
+ * A HTTP request is finished.
+ *
+ * Runs in the I/O thread. The caller must not hold locks.
+ */
+static void
+input_curl_request_done(struct input_curl *c, CURLcode result, long status)
+{
+ assert(io_thread_inside());
+ assert(c != NULL);
+ assert(c->easy == NULL);
+ assert(c->postponed_error == NULL);
+
+ g_mutex_lock(c->mutex);
+
+ if (result != CURLE_OK) {
+ c->postponed_error = g_error_new(curl_quark(), result,
+ "curl failed: %s",
+ c->error);
+ } else if (status < 200 || status >= 300) {
+ c->postponed_error = g_error_new(curl_quark(), 0,
+ "got HTTP status %ld",
+ status);
+ }
+
+ c->base.ready = true;
+ g_cond_broadcast(c->cond);
+ g_mutex_unlock(c->mutex);
+}
+
+static void
+input_curl_handle_done(CURL *easy_handle, CURLcode result)
+{
+ struct input_curl *c = input_curl_find_request(easy_handle);
+ assert(c != NULL);
+
+ long status = 0;
+ curl_easy_getinfo(easy_handle, CURLINFO_RESPONSE_CODE, &status);
+
+ input_curl_easy_free(c);
+ input_curl_request_done(c, result, status);
+}
+
+/**
+ * Check for finished HTTP responses.
+ *
+ * Runs in the I/O thread. The caller must not hold locks.
+ */
+static void
+input_curl_info_read(void)
+{
+ assert(io_thread_inside());
+
+ CURLMsg *msg;
+ int msgs_in_queue;
+
+ while ((msg = curl_multi_info_read(curl.multi,
+ &msgs_in_queue)) != NULL) {
+ if (msg->msg == CURLMSG_DONE)
+ input_curl_handle_done(msg->easy_handle, msg->data.result);
+ }
+}
+
+/**
+ * Give control to CURL.
+ *
+ * Runs in the I/O thread. The caller must not hold locks.
+ */
+static bool
+input_curl_perform(void)
+{
+ assert(io_thread_inside());
+
+ CURLMcode mcode;
+
+ do {
+ int running_handles;
+ mcode = curl_multi_perform(curl.multi, &running_handles);
+ } while (mcode == CURLM_CALL_MULTI_PERFORM);
+
+ if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) {
+ GError *error = g_error_new(curl_quark(), mcode,
+ "curl_multi_perform() failed: %s",
+ curl_multi_strerror(mcode));
+ input_curl_abort_all_requests(error);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * GSource methods
+ *
+ */
+
+/**
+ * The GSource prepare() method implementation.
+ */
+static gboolean
+input_curl_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout_r)
+{
+ curl_update_fds();
+
+#if LIBCURL_VERSION_NUM >= 0x070f04
+ curl.timeout = false;
+
+ long timeout2;
+ CURLMcode mcode = curl_multi_timeout(curl.multi, &timeout2);
+ if (mcode == CURLM_OK) {
+ if (timeout2 >= 0) {
+ g_source_get_current_time(source,
+ &curl.absolute_timeout);
+ g_time_val_add(&curl.absolute_timeout,
+ timeout2 * 1000);
+ }
+
+ if (timeout2 >= 0 && timeout2 < 10)
+ /* CURL 7.21.1 likes to report "timeout=0",
+ which means we're running in a busy loop.
+ Quite a bad idea to waste so much CPU.
+ Let's use a lower limit of 10ms. */
+ timeout2 = 10;
+
+ *timeout_r = timeout2;
+
+ curl.timeout = timeout2 >= 0;
+ } else
+ g_warning("curl_multi_timeout() failed: %s\n",
+ curl_multi_strerror(mcode));
+#else
+ (void)timeout_r;
+#endif
+
+ return false;
+}
+
+/**
+ * The GSource check() method implementation.
+ */
+static gboolean
+input_curl_source_check(G_GNUC_UNUSED GSource *source)
+{
+#if LIBCURL_VERSION_NUM >= 0x070f04
+ if (curl.timeout) {
+ /* when a timeout has expired, we need to call
+ curl_multi_perform(), even if there was no file
+ descriptor event */
+
+ GTimeVal now;
+ g_source_get_current_time(source, &now);
+ if (now.tv_sec > curl.absolute_timeout.tv_sec ||
+ (now.tv_sec == curl.absolute_timeout.tv_sec &&
+ now.tv_usec >= curl.absolute_timeout.tv_usec))
+ return true;
+ }
+#endif
+
+ for (GSList *i = curl.fds; i != NULL; i = i->next) {
+ GPollFD *poll_fd = i->data;
+ if (poll_fd->revents != 0)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * The GSource dispatch() method implementation. The callback isn't
+ * used, because we're handling all events directly.
+ */
+static gboolean
+input_curl_source_dispatch(G_GNUC_UNUSED GSource *source,
+ G_GNUC_UNUSED GSourceFunc callback,
+ G_GNUC_UNUSED gpointer user_data)
+{
+ if (input_curl_perform())
+ input_curl_info_read();
+
+ return true;
+}
+
+/**
+ * The vtable for our GSource implementation. Unfortunately, we
+ * cannot declare it "const", because g_source_new() takes a non-const
+ * pointer, for whatever reason.
+ */
+static GSourceFuncs curl_source_funcs = {
+ .prepare = input_curl_source_prepare,
+ .check = input_curl_source_check,
+ .dispatch = input_curl_source_dispatch,
+};
+
+/*
+ * input_plugin methods
+ *
+ */
+
static bool
input_curl_init(const struct config_param *param,
G_GNUC_UNUSED GError **error_r)
{
CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
if (code != CURLE_OK) {
- g_warning("curl_global_init() failed: %s\n",
- curl_easy_strerror(code));
+ g_set_error(error_r, curl_quark(), code,
+ "curl_global_init() failed: %s\n",
+ curl_easy_strerror(code));
return false;
}
@@ -140,20 +703,58 @@ input_curl_init(const struct config_param *param,
"");
}
+ curl.multi = curl_multi_init();
+ if (curl.multi == NULL) {
+ g_set_error(error_r, curl_quark(), 0,
+ "curl_multi_init() failed");
+ return false;
+ }
+
+ curl.source = g_source_new(&curl_source_funcs, sizeof(*curl.source));
+ curl.source_id = g_source_attach(curl.source, io_thread_context());
+
return true;
}
+static gpointer
+curl_destroy_sources(G_GNUC_UNUSED gpointer data)
+{
+ g_source_destroy(curl.source);
+
+ if (curl.dirty_source_id != 0) {
+ GSource *source =
+ g_main_context_find_source_by_id(io_thread_context(),
+ curl.dirty_source_id);
+ assert(source != NULL);
+ curl.dirty_source_id = 0;
+
+ g_source_destroy(source);
+ }
+
+ return NULL;
+}
+
static void
input_curl_finish(void)
{
+ assert(curl.requests == NULL);
+
+ io_thread_call(curl_destroy_sources, NULL);
+
+ curl_multi_cleanup(curl.multi);
+
curl_slist_free_all(http_200_aliases);
curl_global_cleanup();
}
+#if LIBCURL_VERSION_NUM >= 0x071200
+
/**
* Determine the total sizes of all buffers, including portions that
* have already been consumed.
+ *
+ * The caller must lock the mutex.
*/
G_GNUC_PURE
static size_t
@@ -170,6 +771,8 @@ curl_total_buffer_size(const struct input_curl *c)
return total;
}
+#endif
+
static void
buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
{
@@ -180,31 +783,15 @@ buffer_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
g_free(buffer);
}
-/**
- * Frees the current "libcurl easy" handle, and everything associated
- * with it.
- */
static void
-input_curl_easy_free(struct input_curl *c)
+input_curl_flush_buffers(struct input_curl *c)
{
- if (c->easy != NULL) {
- curl_multi_remove_handle(c->multi, c->easy);
- curl_easy_cleanup(c->easy);
- c->easy = NULL;
- }
-
- curl_slist_free_all(c->request_headers);
- c->request_headers = NULL;
-
- g_free(c->range);
- c->range = NULL;
-
g_queue_foreach(c->buffers, buffer_free_callback, NULL);
g_queue_clear(c->buffers);
}
/**
- * Frees this stream (but not the input_stream struct itself).
+ * Frees this stream, including the input_stream struct.
*/
static void
input_curl_free(struct input_curl *c)
@@ -213,13 +800,14 @@ input_curl_free(struct input_curl *c)
tag_free(c->tag);
g_free(c->meta_name);
- input_curl_easy_free(c);
-
- if (c->multi != NULL)
- curl_multi_cleanup(c->multi);
+ input_curl_easy_free_indirect(c);
+ input_curl_flush_buffers(c);
g_queue_free(c->buffers);
+ g_mutex_free(c->mutex);
+ g_cond_free(c->cond);
+
g_free(c->url);
input_stream_deinit(&c->base);
g_free(c);
@@ -236,119 +824,15 @@ input_curl_tag(struct input_stream *is)
}
static bool
-input_curl_multi_info_read(struct input_curl *c, GError **error_r)
-{
- CURLMsg *msg;
- int msgs_in_queue;
-
- while ((msg = curl_multi_info_read(c->multi,
- &msgs_in_queue)) != NULL) {
- if (msg->msg == CURLMSG_DONE) {
- c->eof = true;
- c->base.ready = true;
-
- if (msg->data.result != CURLE_OK) {
- g_set_error(error_r, curl_quark(),
- msg->data.result,
- "curl failed: %s", c->error);
- return false;
- }
- }
- }
-
- return true;
-}
-
-/**
- * Wait for the libcurl socket.
- *
- * @return -1 on error, 0 if no data is available yet, 1 if data is
- * available
- */
-static int
-input_curl_select(struct input_curl *c, GError **error_r)
+fill_buffer(struct input_curl *c, GError **error_r)
{
- fd_set rfds, wfds, efds;
- int max_fd, ret;
- CURLMcode mcode;
- struct timeval timeout = {
- .tv_sec = 1,
- .tv_usec = 0,
- };
-
- assert(!c->eof);
-
- FD_ZERO(&rfds);
- FD_ZERO(&wfds);
- FD_ZERO(&efds);
+ while (c->easy != NULL && g_queue_is_empty(c->buffers))
+ g_cond_wait(c->cond, c->mutex);
- mcode = curl_multi_fdset(c->multi, &rfds, &wfds, &efds, &max_fd);
- if (mcode != CURLM_OK) {
- g_set_error(error_r, curl_quark(), mcode,
- "curl_multi_fdset() failed: %s",
- curl_multi_strerror(mcode));
- return -1;
- }
-
-#if LIBCURL_VERSION_NUM >= 0x070f04
- long timeout2;
- mcode = curl_multi_timeout(c->multi, &timeout2);
- if (mcode != CURLM_OK) {
- g_warning("curl_multi_timeout() failed: %s\n",
- curl_multi_strerror(mcode));
- return -1;
- }
-
- if (timeout2 >= 0) {
- if (timeout2 > 10000)
- timeout2 = 10000;
-
- timeout.tv_sec = timeout2 / 1000;
- timeout.tv_usec = (timeout2 % 1000) * 1000;
- }
-#endif
-
- ret = select(max_fd + 1, &rfds, &wfds, &efds, &timeout);
- if (ret < 0)
- g_set_error(error_r, g_quark_from_static_string("errno"),
- errno,
- "select() failed: %s\n", g_strerror(errno));
-
- return ret;
-}
-
-static bool
-fill_buffer(struct input_stream *is, GError **error_r)
-{
- struct input_curl *c = (struct input_curl *)is;
- CURLMcode mcode = CURLM_CALL_MULTI_PERFORM;
-
- while (!c->eof && g_queue_is_empty(c->buffers)) {
- int running_handles;
- bool bret;
-
- if (mcode != CURLM_CALL_MULTI_PERFORM) {
- /* if we're still here, there is no input yet
- - wait for input */
- int ret = input_curl_select(c, error_r);
- if (ret <= 0)
- /* no data yet or error */
- return false;
- }
-
- mcode = curl_multi_perform(c->multi, &running_handles);
- if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) {
- g_set_error(error_r, curl_quark(), mcode,
- "curl_multi_perform() failed: %s",
- curl_multi_strerror(mcode));
- c->eof = true;
- is->ready = true;
- return false;
- }
-
- bret = input_curl_multi_info_read(c, error_r);
- if (!bret)
- return false;
+ if (c->postponed_error != NULL) {
+ g_propagate_error(error_r, c->postponed_error);
+ c->postponed_error = NULL;
+ return false;
}
return !g_queue_is_empty(c->buffers);
@@ -453,12 +937,16 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
size_t nbytes = 0;
char *dest = ptr;
+ g_mutex_lock(c->mutex);
+
do {
/* fill the buffer */
- success = fill_buffer(is, error_r);
- if (!success)
+ success = fill_buffer(c, error_r);
+ if (!success) {
+ g_mutex_unlock(c->mutex);
return 0;
+ }
/* send buffer contents */
@@ -476,6 +964,13 @@ input_curl_read(struct input_stream *is, void *ptr, size_t size,
is->offset += (goffset)nbytes;
+#if LIBCURL_VERSION_NUM >= 0x071200
+ if (c->paused && curl_total_buffer_size(c) < CURL_MAX_BUFFERED)
+ io_thread_call(input_curl_resume, c);
+#endif
+
+ g_mutex_unlock(c->mutex);
+
return nbytes;
}
@@ -492,7 +987,11 @@ input_curl_eof(G_GNUC_UNUSED struct input_stream *is)
{
struct input_curl *c = (struct input_curl *)is;
- return c->eof && g_queue_is_empty(c->buffers);
+ g_mutex_lock(c->mutex);
+ bool eof = c->easy == NULL && g_queue_is_empty(c->buffers);
+ g_mutex_unlock(c->mutex);
+
+ return eof;
}
static int
@@ -500,41 +999,21 @@ input_curl_buffer(struct input_stream *is, GError **error_r)
{
struct input_curl *c = (struct input_curl *)is;
- if (curl_total_buffer_size(c) >= CURL_MAX_BUFFERED)
- return 0;
-
- CURLMcode mcode;
- int running_handles;
- bool ret;
-
- c->buffered = false;
-
- if (!is->ready && !c->eof)
- /* not ready yet means the caller is waiting in a busy
- loop; relax that by calling select() on the
- socket */
- if (input_curl_select(c, error_r) < 0)
- return -1;
+ g_mutex_lock(c->mutex);
- do {
- mcode = curl_multi_perform(c->multi, &running_handles);
- } while (mcode == CURLM_CALL_MULTI_PERFORM &&
- g_queue_is_empty(c->buffers));
+ int result;
+ if (c->postponed_error != NULL) {
+ g_propagate_error(error_r, c->postponed_error);
+ c->postponed_error = NULL;
+ result = -1;
+ } else if (g_queue_is_empty(c->buffers))
+ result = 0;
+ else
+ result = 1;
- if (mcode != CURLM_OK && mcode != CURLM_CALL_MULTI_PERFORM) {
- g_set_error(error_r, curl_quark(), mcode,
- "curl_multi_perform() failed: %s",
- curl_multi_strerror(mcode));
- c->eof = true;
- is->ready = true;
- return -1;
- }
-
- ret = input_curl_multi_info_read(c, error_r);
- if (!ret)
- return -1;
+ g_mutex_unlock(c->mutex);
- return c->buffered;
+ return result;
}
/** called by curl when new data is available */
@@ -632,15 +1111,28 @@ input_curl_writefunction(void *ptr, size_t size, size_t nmemb, void *stream)
if (size == 0)
return 0;
+ g_mutex_lock(c->mutex);
+
+#if LIBCURL_VERSION_NUM >= 0x071200
+ if (curl_total_buffer_size(c) + size >= CURL_MAX_BUFFERED) {
+ c->paused = true;
+ g_mutex_unlock(c->mutex);
+ return CURL_WRITEFUNC_PAUSE;
+ }
+#endif
+
buffer = g_malloc(sizeof(*buffer) - sizeof(buffer->data) + size);
buffer->size = size;
buffer->consumed = 0;
memcpy(buffer->data, ptr, size);
+
g_queue_push_tail(c->buffers, buffer);
- c->buffered = true;
c->base.ready = true;
+ g_cond_broadcast(c->cond);
+ g_mutex_unlock(c->mutex);
+
return size;
}
@@ -648,9 +1140,6 @@ static bool
input_curl_easy_init(struct input_curl *c, GError **error_r)
{
CURLcode code;
- CURLMcode mcode;
-
- c->eof = false;
c->easy = curl_easy_init();
if (c->easy == NULL) {
@@ -659,14 +1148,6 @@ input_curl_easy_init(struct input_curl *c, GError **error_r)
return false;
}
- mcode = curl_multi_add_handle(c->multi, c->easy);
- if (mcode != CURLM_OK) {
- g_set_error(error_r, curl_quark(), mcode,
- "curl_multi_add_handle() failed: %s",
- curl_multi_strerror(mcode));
- return false;
- }
-
curl_easy_setopt(c->easy, CURLOPT_USERAGENT,
"Music Player Daemon " VERSION);
curl_easy_setopt(c->easy, CURLOPT_HEADERFUNCTION,
@@ -677,6 +1158,7 @@ input_curl_easy_init(struct input_curl *c, GError **error_r)
curl_easy_setopt(c->easy, CURLOPT_WRITEDATA, c);
curl_easy_setopt(c->easy, CURLOPT_HTTP200ALIASES, http_200_aliases);
curl_easy_setopt(c->easy, CURLOPT_FOLLOWLOCATION, 1);
+ curl_easy_setopt(c->easy, CURLOPT_NETRC, 1);
curl_easy_setopt(c->easy, CURLOPT_MAXREDIRS, 5);
curl_easy_setopt(c->easy, CURLOPT_FAILONERROR, true);
curl_easy_setopt(c->easy, CURLOPT_ERRORBUFFER, c->error);
@@ -713,38 +1195,6 @@ input_curl_easy_init(struct input_curl *c, GError **error_r)
return true;
}
-void
-input_curl_reinit(struct input_stream *is)
-{
- struct input_curl *c = (struct input_curl *)is;
-
- assert(c->base.plugin == &input_plugin_curl);
- assert(c->easy != NULL);
-
- curl_easy_setopt(c->easy, CURLOPT_WRITEHEADER, is);
- curl_easy_setopt(c->easy, CURLOPT_WRITEDATA, is);
-}
-
-static bool
-input_curl_send_request(struct input_curl *c, GError **error_r)
-{
- CURLMcode mcode;
- int running_handles;
-
- do {
- mcode = curl_multi_perform(c->multi, &running_handles);
- } while (mcode == CURLM_CALL_MULTI_PERFORM);
-
- if (mcode != CURLM_OK) {
- g_set_error(error_r, curl_quark(), mcode,
- "curl_multi_perform() failed: %s",
- curl_multi_strerror(mcode));
- return false;
- }
-
- return true;
-}
-
static bool
input_curl_seek(struct input_stream *is, goffset offset, int whence,
GError **error_r)
@@ -788,6 +1238,8 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
/* check if we can fast-forward the buffer */
+ g_mutex_lock(c->mutex);
+
while (offset > is->offset && !g_queue_is_empty(c->buffers)) {
struct buffer *buffer;
size_t length;
@@ -805,19 +1257,21 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
is->offset += length;
}
+ g_mutex_unlock(c->mutex);
+
if (offset == is->offset)
return true;
/* close the old connection and open a new one */
- input_curl_easy_free(c);
+ input_curl_easy_free_indirect(c);
+ input_curl_flush_buffers(c);
is->offset = offset;
if (is->offset == is->size) {
/* seek to EOF: simulate empty result; avoid
triggering a "416 Requested Range Not Satisfiable"
response */
- c->eof = true;
return true;
}
@@ -832,18 +1286,32 @@ input_curl_seek(struct input_stream *is, goffset offset, int whence,
curl_easy_setopt(c->easy, CURLOPT_RANGE, c->range);
}
- ret = input_curl_send_request(c, error_r);
- if (!ret)
+ c->base.ready = false;
+
+ if (!input_curl_easy_add_indirect(c, error_r))
return false;
- return input_curl_multi_info_read(c, error_r);
+ g_mutex_lock(c->mutex);
+
+ while (!c->base.ready)
+ g_cond_wait(c->cond, c->mutex);
+
+ if (c->postponed_error != NULL) {
+ g_propagate_error(error_r, c->postponed_error);
+ c->postponed_error = NULL;
+ g_mutex_unlock(c->mutex);
+ return false;
+ }
+
+ g_mutex_unlock(c->mutex);
+
+ return true;
}
static struct input_stream *
input_curl_open(const char *url, GError **error_r)
{
struct input_curl *c;
- bool ret;
if (strncmp(url, "http://", 7) != 0)
return NULL;
@@ -851,34 +1319,25 @@ input_curl_open(const char *url, GError **error_r)
c = g_new0(struct input_curl, 1);
input_stream_init(&c->base, &input_plugin_curl, url);
+ c->mutex = g_mutex_new();
+ c->cond = g_cond_new();
+
c->url = g_strdup(url);
c->buffers = g_queue_new();
- c->multi = curl_multi_init();
- if (c->multi == NULL) {
- g_set_error(error_r, curl_quark(), 0,
- "curl_multi_init() failed");
- input_curl_free(c);
- return NULL;
- }
-
icy_clear(&c->icy_metadata);
c->tag = NULL;
- ret = input_curl_easy_init(c, error_r);
- if (!ret) {
- input_curl_free(c);
- return NULL;
- }
+#if LIBCURL_VERSION_NUM >= 0x071200
+ c->paused = false;
+#endif
- ret = input_curl_send_request(c, error_r);
- if (!ret) {
+ if (!input_curl_easy_init(c, error_r)) {
input_curl_free(c);
return NULL;
}
- ret = input_curl_multi_info_read(c, error_r);
- if (!ret) {
+ if (!input_curl_easy_add_indirect(c, error_r)) {
input_curl_free(c);
return NULL;
}
diff --git a/src/input/curl_input_plugin.h b/src/input/curl_input_plugin.h
index be7db4e26..c6e71bf40 100644
--- a/src/input/curl_input_plugin.h
+++ b/src/input/curl_input_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,12 +24,4 @@ struct input_stream;
extern const struct input_plugin input_plugin_curl;
-/**
- * This is a workaround for an input_stream API deficiency; after
- * exchanging the input_stream pointer in input_rewind_open(), this
- * function is called to reinitialize CURL's data pointers.
- */
-void
-input_curl_reinit(struct input_stream *is);
-
#endif
diff --git a/src/input/despotify_input_plugin.c b/src/input/despotify_input_plugin.c
new file mode 100644
index 000000000..ef78fb1e0
--- /dev/null
+++ b/src/input/despotify_input_plugin.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include "config.h"
+#include "input/despotify_input_plugin.h"
+#include "input_internal.h"
+#include "input_plugin.h"
+#include "tag.h"
+#include "despotify_utils.h"
+
+#include <glib.h>
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <despotify.h>
+
+#include <stdio.h>
+
+struct input_despotify {
+ struct input_stream base;
+
+ struct despotify_session *session;
+ struct ds_track *track;
+ struct tag *tag;
+ struct ds_pcm_data pcm;
+ size_t len_available;
+ bool eof;
+};
+
+
+static void
+refill_buffer(struct input_despotify *ctx)
+{
+ /* Wait until there is data */
+ while (1) {
+ int rc = despotify_get_pcm(ctx->session, &ctx->pcm);
+
+ if (rc == 0 && ctx->pcm.len) {
+ ctx->len_available = ctx->pcm.len;
+ break;
+ }
+ if (ctx->eof == true)
+ break;
+
+ if (rc < 0) {
+ g_debug("despotify_get_pcm error\n");
+ ctx->eof = true;
+ break;
+ }
+
+ /* Wait a while until next iteration */
+ usleep(50 * 1000);
+ }
+}
+
+static void callback(G_GNUC_UNUSED struct despotify_session* ds,
+ int sig, G_GNUC_UNUSED void* data, void* callback_data)
+{
+ struct input_despotify *ctx = (struct input_despotify *)callback_data;
+
+ switch (sig) {
+ case DESPOTIFY_NEW_TRACK:
+ break;
+
+ case DESPOTIFY_TIME_TELL:
+ break;
+
+ case DESPOTIFY_TRACK_PLAY_ERROR:
+ g_debug("Track play error\n");
+ ctx->eof = true;
+ ctx->len_available = 0;
+ break;
+
+ case DESPOTIFY_END_OF_PLAYLIST:
+ ctx->eof = true;
+ g_debug("End of playlist: %d\n", ctx->eof);
+ break;
+ }
+}
+
+
+static struct input_stream *
+input_despotify_open(const char *url, G_GNUC_UNUSED GError **error_r)
+{
+ struct input_despotify *ctx;
+ struct despotify_session *session;
+ struct ds_link *ds_link;
+ struct ds_track *track;
+
+ if (!g_str_has_prefix(url, "spt://"))
+ return NULL;
+
+ session = mpd_despotify_get_session();
+ if (!session)
+ return NULL;
+
+ ds_link = despotify_link_from_uri(url + 6);
+ if (!ds_link) {
+ g_debug("Can't find %s\n", url);
+ return NULL;
+ }
+ if (ds_link->type != LINK_TYPE_TRACK) {
+ despotify_free_link(ds_link);
+ return NULL;
+ }
+
+ ctx = g_new(struct input_despotify, 1);
+ memset(ctx, 0, sizeof(*ctx));
+
+ track = despotify_link_get_track(session, ds_link);
+ despotify_free_link(ds_link);
+ if (!track) {
+ g_free(ctx);
+ return NULL;
+ }
+
+ input_stream_init(&ctx->base, &input_plugin_despotify, url);
+ ctx->session = session;
+ ctx->track = track;
+ ctx->tag = mpd_despotify_tag_from_track(track);
+ ctx->eof = false;
+ /* Despotify outputs pcm data */
+ ctx->base.mime = g_strdup("audio/x-mpd-cdda-pcm");
+ ctx->base.ready = true;
+
+ if (!mpd_despotify_register_callback(callback, ctx)) {
+ despotify_free_link(ds_link);
+
+ return NULL;
+ }
+
+ if (despotify_play(ctx->session, ctx->track, false) == false) {
+ despotify_free_track(ctx->track);
+ g_free(ctx);
+ return NULL;
+ }
+
+ return &ctx->base;
+}
+
+static size_t
+input_despotify_read(struct input_stream *is, void *ptr, size_t size,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct input_despotify *ctx = (struct input_despotify *)is;
+ size_t to_cpy = size;
+
+ if (ctx->len_available == 0)
+ refill_buffer(ctx);
+
+ if (ctx->len_available < size)
+ to_cpy = ctx->len_available;
+ memcpy(ptr, ctx->pcm.buf, to_cpy);
+ ctx->len_available -= to_cpy;
+
+ is->offset += to_cpy;
+
+ return to_cpy;
+}
+
+static void
+input_despotify_close(struct input_stream *is)
+{
+ struct input_despotify *ctx = (struct input_despotify *)is;
+
+ if (ctx->tag != NULL)
+ tag_free(ctx->tag);
+
+ mpd_despotify_unregister_callback(callback);
+ despotify_free_track(ctx->track);
+ input_stream_deinit(&ctx->base);
+ g_free(ctx);
+}
+
+static bool
+input_despotify_eof(struct input_stream *is)
+{
+ struct input_despotify *ctx = (struct input_despotify *)is;
+
+ return ctx->eof;
+}
+
+static bool
+input_despotify_seek(G_GNUC_UNUSED struct input_stream *is,
+ G_GNUC_UNUSED goffset offset, G_GNUC_UNUSED int whence,
+ G_GNUC_UNUSED GError **error_r)
+{
+ return false;
+}
+
+static struct tag *
+input_despotify_tag(struct input_stream *is)
+{
+ struct input_despotify *ctx = (struct input_despotify *)is;
+ struct tag *tag = ctx->tag;
+
+ ctx->tag = NULL;
+
+ return tag;
+}
+
+const struct input_plugin input_plugin_despotify = {
+ .name = "spt",
+ .open = input_despotify_open,
+ .close = input_despotify_close,
+ .read = input_despotify_read,
+ .eof = input_despotify_eof,
+ .seek = input_despotify_seek,
+ .tag = input_despotify_tag,
+};
diff --git a/src/input/despotify_input_plugin.h b/src/input/despotify_input_plugin.h
new file mode 100644
index 000000000..4c070d882
--- /dev/null
+++ b/src/input/despotify_input_plugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 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 INPUT_DESPOTIFY_H
+#define INPUT_DESPOTIFY_H
+
+extern const struct input_plugin input_plugin_despotify;
+
+#endif
diff --git a/src/input/ffmpeg_input_plugin.c b/src/input/ffmpeg_input_plugin.c
index 0a6be29bc..6caa7ea04 100644
--- a/src/input/ffmpeg_input_plugin.c
+++ b/src/input/ffmpeg_input_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,15 +19,11 @@
#include "config.h"
#include "input/ffmpeg_input_plugin.h"
+#include "input_internal.h"
#include "input_plugin.h"
-#ifdef OLD_FFMPEG_INCLUDES
-#include <avio.h>
-#include <avformat.h>
-#else
#include <libavformat/avio.h>
#include <libavformat/avformat.h>
-#endif
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "input_ffmpeg"
@@ -35,7 +31,11 @@
struct input_ffmpeg {
struct input_stream base;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ AVIOContext *h;
+#else
URLContext *h;
+#endif
bool eof;
};
@@ -46,20 +46,29 @@ ffmpeg_quark(void)
return g_quark_from_static_string("ffmpeg");
}
+static inline bool
+input_ffmpeg_supported(void)
+{
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ void *opaque = NULL;
+ return avio_enum_protocols(&opaque, 0) != NULL;
+#else
+ return av_protocol_next(NULL) != NULL;
+#endif
+}
+
static bool
input_ffmpeg_init(G_GNUC_UNUSED const struct config_param *param,
G_GNUC_UNUSED GError **error_r)
{
av_register_all();
-#if LIBAVFORMAT_VERSION_MAJOR >= 52
/* disable this plugin if there's no registered protocol */
- if (av_protocol_next(NULL) == NULL) {
+ if (!input_ffmpeg_supported()) {
g_set_error(error_r, ffmpeg_quark(), 0,
"No protocol");
return false;
}
-#endif
return true;
}
@@ -80,7 +89,13 @@ input_ffmpeg_open(const char *uri, GError **error_r)
i = g_new(struct input_ffmpeg, 1);
input_stream_init(&i->base, &input_plugin_ffmpeg, uri);
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,1,0)
+ int ret = avio_open(&i->h, uri, AVIO_FLAG_READ);
+#elif LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ int ret = avio_open(&i->h, uri, AVIO_RDONLY);
+#else
int ret = url_open(&i->h, uri, URL_RDONLY);
+#endif
if (ret != 0) {
g_free(i);
g_set_error(error_r, ffmpeg_quark(), ret,
@@ -91,8 +106,13 @@ input_ffmpeg_open(const char *uri, GError **error_r)
i->eof = false;
i->base.ready = true;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ i->base.seekable = (i->h->seekable & AVIO_SEEKABLE_NORMAL) != 0;
+ i->base.size = avio_size(i->h);
+#else
i->base.seekable = !i->h->is_streamed;
i->base.size = url_filesize(i->h);
+#endif
/* hack to make MPD select the "ffmpeg" decoder plugin - since
avio.h doesn't tell us the MIME type of the resource, we
@@ -109,7 +129,11 @@ input_ffmpeg_read(struct input_stream *is, void *ptr, size_t size,
{
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ int ret = avio_read(i->h, ptr, size);
+#else
int ret = url_read(i->h, ptr, size);
+#endif
if (ret <= 0) {
if (ret < 0)
g_set_error(error_r, ffmpeg_quark(), 0,
@@ -128,7 +152,11 @@ input_ffmpeg_close(struct input_stream *is)
{
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ avio_close(i->h);
+#else
url_close(i->h);
+#endif
input_stream_deinit(&i->base);
g_free(i);
}
@@ -146,7 +174,11 @@ input_ffmpeg_seek(struct input_stream *is, goffset offset, int whence,
G_GNUC_UNUSED GError **error_r)
{
struct input_ffmpeg *i = (struct input_ffmpeg *)is;
+#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53,0,0)
+ int64_t ret = avio_seek(i->h, offset, whence);
+#else
int64_t ret = url_seek(i->h, offset, whence);
+#endif
if (ret >= 0) {
i->eof = false;
diff --git a/src/input/ffmpeg_input_plugin.h b/src/input/ffmpeg_input_plugin.h
index ff87064be..393836ca5 100644
--- a/src/input/ffmpeg_input_plugin.h
+++ b/src/input/ffmpeg_input_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input/file_input_plugin.c b/src/input/file_input_plugin.c
index 3646c656e..10b753d24 100644
--- a/src/input/file_input_plugin.c
+++ b/src/input/file_input_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "config.h" /* must be first for large file support */
#include "input/file_input_plugin.h"
+#include "input_internal.h"
#include "input_plugin.h"
#include "fd_util.h"
#include "open.h"
@@ -52,7 +53,7 @@ input_file_open(const char *filename, GError **error_r)
struct file_input_stream *fis;
if (!g_path_is_absolute(filename))
- return false;
+ return NULL;
fd = open_cloexec(filename, O_RDONLY|O_BINARY, 0);
if (fd < 0) {
@@ -60,7 +61,7 @@ input_file_open(const char *filename, GError **error_r)
g_set_error(error_r, file_quark(), errno,
"Failed to open \"%s\": %s",
filename, g_strerror(errno));
- return false;
+ return NULL;
}
ret = fstat(fd, &st);
@@ -69,14 +70,14 @@ input_file_open(const char *filename, GError **error_r)
"Failed to stat \"%s\": %s",
filename, g_strerror(errno));
close(fd);
- return false;
+ return NULL;
}
if (!S_ISREG(st.st_mode)) {
g_set_error(error_r, file_quark(), 0,
"Not a regular file: %s", filename);
close(fd);
- return false;
+ return NULL;
}
#ifdef POSIX_FADV_SEQUENTIAL
diff --git a/src/input/file_input_plugin.h b/src/input/file_input_plugin.h
index 40340e8bd..f24769d57 100644
--- a/src/input/file_input_plugin.h
+++ b/src/input/file_input_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input/mms_input_plugin.c b/src/input/mms_input_plugin.c
index 834d111b8..eb3e5d26c 100644
--- a/src/input/mms_input_plugin.c
+++ b/src/input/mms_input_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "config.h"
#include "input/mms_input_plugin.h"
+#include "input_internal.h"
#include "input_plugin.h"
#include <glib.h>
diff --git a/src/input/mms_input_plugin.h b/src/input/mms_input_plugin.h
index 2e10cfbb9..d6aa593f2 100644
--- a/src/input/mms_input_plugin.h
+++ b/src/input/mms_input_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input/rewind_input_plugin.c b/src/input/rewind_input_plugin.c
index f0d533bc8..fa2065d61 100644
--- a/src/input/rewind_input_plugin.c
+++ b/src/input/rewind_input_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,7 +19,7 @@
#include "config.h"
#include "input/rewind_input_plugin.h"
-#include "input/curl_input_plugin.h"
+#include "input_internal.h"
#include "input_plugin.h"
#include "tag.h"
@@ -107,6 +107,15 @@ input_rewind_close(struct input_stream *is)
g_free(r);
}
+static void
+input_rewind_update(struct input_stream *is)
+{
+ struct input_rewind *r = (struct input_rewind *)is;
+
+ if (!reading_from_buffer(r))
+ copy_attributes(r);
+}
+
static struct tag *
input_rewind_tag(struct input_stream *is)
{
@@ -212,6 +221,7 @@ input_rewind_seek(struct input_stream *is, goffset offset, int whence,
static const struct input_plugin rewind_input_plugin = {
.close = input_rewind_close,
+ .update = input_rewind_update,
.tag = input_rewind_tag,
.buffer = input_rewind_buffer,
.read = input_rewind_read,
diff --git a/src/input/rewind_input_plugin.h b/src/input/rewind_input_plugin.h
index 23d25d94d..83abe257a 100644
--- a/src/input/rewind_input_plugin.h
+++ b/src/input/rewind_input_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input/soup_input_plugin.c b/src/input/soup_input_plugin.c
new file mode 100644
index 000000000..81fe4a441
--- /dev/null
+++ b/src/input/soup_input_plugin.c
@@ -0,0 +1,381 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "input/soup_input_plugin.h"
+#include "input_internal.h"
+#include "input_plugin.h"
+#include "io_thread.h"
+#include "conf.h"
+
+#include <libsoup/soup-uri.h>
+#include <libsoup/soup-session-async.h>
+
+#include <assert.h>
+#include <string.h>
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "input_soup"
+
+/**
+ * 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 SOUP_MAX_BUFFERED = 512 * 1024;
+
+static SoupURI *soup_proxy;
+static SoupSession *soup_session;
+
+struct input_soup {
+ struct input_stream base;
+
+ GMutex *mutex;
+ GCond *cond;
+
+ SoupMessage *msg;
+
+ GQueue *buffers;
+
+ size_t current_consumed;
+
+ size_t total_buffered;
+
+ bool alive, ready, pause, eof;
+};
+
+static inline GQuark
+soup_quark(void)
+{
+ return g_quark_from_static_string("soup");
+}
+
+static bool
+input_soup_init(const struct config_param *param, GError **error_r)
+{
+ assert(soup_proxy == NULL);
+ assert(soup_session == NULL);
+
+ g_type_init();
+
+ const char *proxy = config_get_block_string(param, "proxy", NULL);
+
+ if (proxy != NULL) {
+ soup_proxy = soup_uri_new(proxy);
+ if (soup_proxy == NULL) {
+ g_set_error(error_r, soup_quark(), 0,
+ "failed to parse proxy setting");
+ return false;
+ }
+ }
+
+ soup_session =
+ soup_session_async_new_with_options(SOUP_SESSION_PROXY_URI,
+ soup_proxy,
+ SOUP_SESSION_ASYNC_CONTEXT,
+ io_thread_context(),
+ NULL);
+
+ return true;
+}
+
+static void
+input_soup_finish(void)
+{
+ assert(soup_session != NULL);
+
+ soup_session_abort(soup_session);
+ g_object_unref(G_OBJECT(soup_session));
+
+ if (soup_proxy != NULL)
+ soup_uri_free(soup_proxy);
+}
+
+static void
+input_soup_session_callback(G_GNUC_UNUSED SoupSession *session,
+ G_GNUC_UNUSED SoupMessage *msg, gpointer user_data)
+{
+ struct input_soup *s = user_data;
+
+ assert(msg == s->msg);
+
+ g_mutex_lock(s->mutex);
+ s->alive = false;
+ g_cond_broadcast(s->cond);
+ g_mutex_unlock(s->mutex);
+}
+
+static void
+input_soup_got_headers(SoupMessage *msg, gpointer user_data)
+{
+ struct input_soup *s = user_data;
+
+ if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) {
+ soup_session_cancel_message(soup_session, msg,
+ SOUP_STATUS_CANCELLED);
+ return;
+ }
+
+ soup_message_body_set_accumulate(msg->response_body, false);
+
+ g_mutex_lock(s->mutex);
+ s->ready = true;
+ g_cond_broadcast(s->cond);
+ g_mutex_unlock(s->mutex);
+}
+
+static void
+input_soup_got_chunk(SoupMessage *msg, SoupBuffer *chunk, gpointer user_data)
+{
+ struct input_soup *s = user_data;
+
+ assert(msg == s->msg);
+
+ g_mutex_lock(s->mutex);
+
+ g_queue_push_tail(s->buffers, soup_buffer_copy(chunk));
+ s->total_buffered += chunk->length;
+
+ if (s->total_buffered >= SOUP_MAX_BUFFERED && !s->pause) {
+ s->pause = true;
+ soup_session_pause_message(soup_session, msg);
+ }
+
+ g_cond_broadcast(s->cond);
+ g_mutex_unlock(s->mutex);
+}
+
+static void
+input_soup_got_body(SoupMessage *msg, gpointer user_data)
+{
+ struct input_soup *s = user_data;
+
+ assert(msg == s->msg);
+
+ g_mutex_lock(s->mutex);
+
+ s->eof = true;
+ s->alive = false;
+
+ g_cond_broadcast(s->cond);
+ g_mutex_unlock(s->mutex);
+}
+
+static bool
+input_soup_wait_data(struct input_soup *s)
+{
+ while (true) {
+ if (s->eof)
+ return true;
+
+ if (!s->alive)
+ return false;
+
+ if (!g_queue_is_empty(s->buffers))
+ return true;
+
+ assert(s->current_consumed == 0);
+
+ g_cond_wait(s->cond, s->mutex);
+ }
+}
+
+static struct input_stream *
+input_soup_open(const char *uri, G_GNUC_UNUSED GError **error_r)
+{
+ if (strncmp(uri, "http://", 7) != 0)
+ return NULL;
+
+ struct input_soup *s = g_new(struct input_soup, 1);
+ input_stream_init(&s->base, &input_plugin_soup, uri);
+
+ s->mutex = g_mutex_new();
+ s->cond = g_cond_new();
+
+ s->buffers = g_queue_new();
+ s->current_consumed = 0;
+ s->total_buffered = 0;
+
+ s->msg = soup_message_new(SOUP_METHOD_GET, uri);
+ soup_message_set_flags(s->msg, SOUP_MESSAGE_NO_REDIRECT);
+
+ soup_message_headers_append(s->msg->request_headers, "User-Agent",
+ "Music Player Daemon " VERSION);
+
+ g_signal_connect(s->msg, "got-headers",
+ G_CALLBACK(input_soup_got_headers), s);
+ g_signal_connect(s->msg, "got-chunk",
+ G_CALLBACK(input_soup_got_chunk), s);
+ g_signal_connect(s->msg, "got-body",
+ G_CALLBACK(input_soup_got_body), s);
+
+ s->alive = true;
+ s->ready = false;
+ s->pause = false;
+ s->eof = false;
+
+ soup_session_queue_message(soup_session, s->msg,
+ input_soup_session_callback, s);
+
+ return &s->base;
+}
+
+static void
+input_soup_close(struct input_stream *is)
+{
+ struct input_soup *s = (struct input_soup *)is;
+
+ g_mutex_lock(s->mutex);
+
+ if (s->alive) {
+ assert(s->msg != NULL);
+
+ soup_session_cancel_message(soup_session, s->msg,
+ SOUP_STATUS_CANCELLED);
+ s->alive = false;
+ }
+
+ g_mutex_unlock(s->mutex);
+ g_mutex_free(s->mutex);
+ g_cond_free(s->cond);
+
+ SoupBuffer *buffer;
+ while ((buffer = g_queue_pop_head(s->buffers)) != NULL)
+ soup_buffer_free(buffer);
+ g_queue_free(s->buffers);
+
+ input_stream_deinit(&s->base);
+ g_free(s);
+}
+
+static int
+input_soup_buffer(struct input_stream *is, GError **error_r)
+{
+ struct input_soup *s = (struct input_soup *)is;
+
+ g_mutex_lock(s->mutex);
+
+ if (s->pause) {
+ if (s->total_buffered >= SOUP_MAX_BUFFERED) {
+ g_mutex_unlock(s->mutex);
+ return 1;
+ }
+
+ s->pause = false;
+ soup_session_unpause_message(soup_session, s->msg);
+ }
+
+
+ bool success = input_soup_wait_data(s);
+ s->base.ready = s->ready;
+ g_mutex_unlock(s->mutex);
+
+ if (!success) {
+ g_set_error_literal(error_r, soup_quark(), 0, "HTTP failure");
+ return -1;
+ }
+
+ return 1;
+}
+
+static size_t
+input_soup_read(struct input_stream *is, void *ptr, size_t size,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct input_soup *s = (struct input_soup *)is;
+
+ g_mutex_lock(s->mutex);
+
+ if (!input_soup_wait_data(s)) {
+ assert(!s->alive);
+ g_mutex_unlock(s->mutex);
+
+ return 0;
+ }
+
+ s->base.ready = s->ready;
+
+ char *p0 = ptr, *p = p0, *p_end = p0 + size;
+
+ while (p < p_end) {
+ SoupBuffer *buffer = g_queue_pop_head(s->buffers);
+ if (buffer == NULL) {
+ assert(s->current_consumed == 0);
+ break;
+ }
+
+ assert(s->current_consumed < buffer->length);
+ assert(s->total_buffered >= buffer->length);
+
+ const char *q = buffer->data;
+ q += s->current_consumed;
+
+ size_t remaining = buffer->length - s->current_consumed;
+ size_t nbytes = p_end - p;
+ if (nbytes > remaining)
+ nbytes = remaining;
+
+ memcpy(p, q, nbytes);
+ p += nbytes;
+
+ s->current_consumed += remaining;
+ if (s->current_consumed >= buffer->length) {
+ /* done with this buffer */
+ s->total_buffered -= buffer->length;
+ soup_buffer_free(buffer);
+ s->current_consumed = 0;
+ } else {
+ /* partial read */
+ assert(p == p_end);
+
+ g_queue_push_head(s->buffers, buffer);
+ }
+ }
+
+ if (s->pause && s->total_buffered < SOUP_MAX_BUFFERED) {
+ s->pause = false;
+ soup_session_unpause_message(soup_session, s->msg);
+ }
+
+ size_t nbytes = p - p0;
+ s->base.offset += nbytes;
+
+ g_mutex_unlock(s->mutex);
+ return nbytes;
+}
+
+static bool
+input_soup_eof(G_GNUC_UNUSED struct input_stream *is)
+{
+ struct input_soup *s = (struct input_soup *)is;
+
+ return !s->alive && g_queue_is_empty(s->buffers);
+}
+
+const struct input_plugin input_plugin_soup = {
+ .name = "soup",
+ .init = input_soup_init,
+ .finish = input_soup_finish,
+
+ .open = input_soup_open,
+ .close = input_soup_close,
+ .buffer = input_soup_buffer,
+ .read = input_soup_read,
+ .eof = input_soup_eof,
+};
diff --git a/src/input/soup_input_plugin.h b/src/input/soup_input_plugin.h
new file mode 100644
index 000000000..689b2d971
--- /dev/null
+++ b/src/input/soup_input_plugin.h
@@ -0,0 +1,25 @@
+/*
+ * 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_INPUT_SOUP_H
+#define MPD_INPUT_SOUP_H
+
+extern const struct input_plugin input_plugin_soup;
+
+#endif
diff --git a/src/input_init.c b/src/input_init.c
index 1438c3e52..cf5affb4e 100644
--- a/src/input_init.c
+++ b/src/input_init.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include "conf.h"
#include "glib_compat.h"
+#include <assert.h>
#include <string.h>
static inline GQuark
@@ -67,6 +68,11 @@ input_stream_global_init(GError **error_r)
for (unsigned i = 0; input_plugins[i] != NULL; ++i) {
const struct input_plugin *plugin = input_plugins[i];
+
+ assert(plugin->name != NULL);
+ assert(*plugin->name != 0);
+ assert(plugin->open != NULL);
+
const struct config_param *param =
input_plugin_config(plugin->name, &error);
if (param == NULL && error != NULL) {
diff --git a/src/input_init.h b/src/input_init.h
index eded15fa9..ad92cda08 100644
--- a/src/input_init.h
+++ b/src/input_init.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -28,7 +28,7 @@
/**
* Initializes this library and all input_stream implementations.
*
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
*/
bool
diff --git a/src/input_internal.c b/src/input_internal.c
new file mode 100644
index 000000000..4d675fc97
--- /dev/null
+++ b/src/input_internal.c
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "input_internal.h"
+#include "input_stream.h"
+
+#include <assert.h>
+
+void
+input_stream_init(struct input_stream *is, const struct input_plugin *plugin,
+ const char *uri)
+{
+ assert(is != NULL);
+ assert(plugin != NULL);
+ assert(uri != NULL);
+
+ is->plugin = plugin;
+ is->uri = g_strdup(uri);
+ is->ready = false;
+ is->seekable = false;
+ is->size = -1;
+ is->offset = 0;
+ is->mime = NULL;
+}
+
+void
+input_stream_deinit(struct input_stream *is)
+{
+ assert(is != NULL);
+ assert(is->plugin != NULL);
+
+ g_free(is->uri);
+ g_free(is->mime);
+}
diff --git a/src/input_internal.h b/src/input_internal.h
new file mode 100644
index 000000000..260ea12a6
--- /dev/null
+++ b/src/input_internal.h
@@ -0,0 +1,35 @@
+/*
+ * 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_INPUT_INTERNAL_H
+#define MPD_INPUT_INTERNAL_H
+
+#include "check.h"
+
+struct input_stream;
+struct input_plugin;
+
+void
+input_stream_init(struct input_stream *is, const struct input_plugin *plugin,
+ const char *uri);
+
+void
+input_stream_deinit(struct input_stream *is);
+
+#endif
diff --git a/src/input_plugin.h b/src/input_plugin.h
index 10be48dbb..3ac0bdf40 100644
--- a/src/input_plugin.h
+++ b/src/input_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -35,7 +35,7 @@ struct input_plugin {
/**
* Global initialization. This method is called when MPD starts.
*
- * @param error_r location to store the error occuring, or
+ * @param error_r location to store the error occurring, or
* NULL to ignore errors
* @return true on success, false if the plugin should be
* disabled
@@ -51,6 +51,12 @@ struct input_plugin {
struct input_stream *(*open)(const char *uri, GError **error_r);
void (*close)(struct input_stream *is);
+ /**
+ * Update the public attributes. Call before access. Can be
+ * NULL if the plugin always keeps its attributes up to date.
+ */
+ void (*update)(struct input_stream *is);
+
struct tag *(*tag)(struct input_stream *is);
int (*buffer)(struct input_stream *is, GError **error_r);
size_t (*read)(struct input_stream *is, void *ptr, size_t size,
diff --git a/src/input_registry.c b/src/input_registry.c
index 0b9b47d10..5987d5da2 100644
--- a/src/input_registry.c
+++ b/src/input_registry.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,10 @@
#include "input/curl_input_plugin.h"
#endif
+#ifdef ENABLE_SOUP
+#include "input/soup_input_plugin.h"
+#endif
+
#ifdef HAVE_FFMPEG
#include "input/ffmpeg_input_plugin.h"
#endif
@@ -37,6 +41,14 @@
#include "input/mms_input_plugin.h"
#endif
+#ifdef ENABLE_CDIO_PARANOIA
+#include "input/cdio_paranoia_input_plugin.h"
+#endif
+
+#ifdef ENABLE_DESPOTIFY
+#include "input/despotify_input_plugin.h"
+#endif
+
#include <glib.h>
const struct input_plugin *const input_plugins[] = {
@@ -47,12 +59,21 @@ const struct input_plugin *const input_plugins[] = {
#ifdef ENABLE_CURL
&input_plugin_curl,
#endif
+#ifdef ENABLE_SOUP
+ &input_plugin_soup,
+#endif
#ifdef HAVE_FFMPEG
&input_plugin_ffmpeg,
#endif
#ifdef ENABLE_MMS
&input_plugin_mms,
#endif
+#ifdef ENABLE_CDIO_PARANOIA
+ &input_plugin_cdio_paranoia,
+#endif
+#ifdef ENABLE_DESPOTIFY
+ &input_plugin_despotify,
+#endif
NULL
};
diff --git a/src/input_registry.h b/src/input_registry.h
index e85d6be8e..a1b057469 100644
--- a/src/input_registry.h
+++ b/src/input_registry.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/input_stream.c b/src/input_stream.c
index e769adb92..44ab7159f 100644
--- a/src/input_stream.c
+++ b/src/input_stream.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -64,7 +64,17 @@ input_stream_open(const char *url, GError **error_r)
}
g_set_error(error_r, input_quark(), 0, "Unrecognized URI");
- return false;
+ return NULL;
+}
+
+void
+input_stream_update(struct input_stream *is)
+{
+ assert(is != NULL);
+ assert(is->plugin != NULL);
+
+ if (is->plugin->update != NULL)
+ is->plugin->update(is);
}
bool
diff --git a/src/input_stream.h b/src/input_stream.h
index 056d008a7..2901f6ea6 100644
--- a/src/input_stream.h
+++ b/src/input_stream.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
#define MPD_INPUT_STREAM_H
#include "check.h"
+#include "gcc.h"
#include <glib.h>
@@ -71,42 +72,33 @@ struct input_stream {
char *mime;
};
-static inline void
-input_stream_init(struct input_stream *is, const struct input_plugin *plugin,
- const char *uri)
-{
- is->plugin = plugin;
- is->uri = g_strdup(uri);
- is->ready = false;
- is->seekable = false;
- is->size = -1;
- is->offset = 0;
- is->mime = NULL;
-}
-
-static inline void
-input_stream_deinit(struct input_stream *is)
-{
- g_free(is->uri);
- g_free(is->mime);
-}
-
/**
* Opens a new input stream. You may not access it until the "ready"
* flag is set.
*
* @return an #input_stream object on success, NULL on error
*/
+gcc_nonnull(1)
+G_GNUC_MALLOC
struct input_stream *
input_stream_open(const char *uri, GError **error_r);
/**
* Close the input stream and free resources.
*/
+gcc_nonnull(1)
void
input_stream_close(struct input_stream *is);
/**
+ * Update the public attributes. Call before accessing attributes
+ * such as "ready" or "offset".
+ */
+gcc_nonnull(1)
+void
+input_stream_update(struct input_stream *is);
+
+/**
* Seeks to the specified position in the stream. This will most
* likely fail if the "seekable" flag is false.
*
@@ -114,6 +106,7 @@ input_stream_close(struct input_stream *is);
* @param offset the relative offset
* @param whence the base of the seek, one of SEEK_SET, SEEK_CUR, SEEK_END
*/
+gcc_nonnull(1)
bool
input_stream_seek(struct input_stream *is, goffset offset, int whence,
GError **error_r);
@@ -121,6 +114,8 @@ input_stream_seek(struct input_stream *is, goffset offset, int whence,
/**
* Returns true if the stream has reached end-of-file.
*/
+gcc_nonnull(1)
+G_GNUC_PURE
bool input_stream_eof(struct input_stream *is);
/**
@@ -129,6 +124,8 @@ bool input_stream_eof(struct input_stream *is);
* @return a tag object which must be freed with tag_free(), or NULL
* if the tag has not changed since the last call
*/
+gcc_nonnull(1)
+G_GNUC_MALLOC
struct tag *
input_stream_tag(struct input_stream *is);
@@ -140,6 +137,7 @@ input_stream_tag(struct input_stream *is);
* The semantics of this function are not well-defined, and it will
* eventually be removed.
*/
+gcc_nonnull(1)
int input_stream_buffer(struct input_stream *is, GError **error_r);
/**
@@ -151,6 +149,7 @@ int input_stream_buffer(struct input_stream *is, GError **error_r);
* @param size the maximum number of bytes to read
* @return the number of bytes read
*/
+gcc_nonnull(1, 2)
size_t
input_stream_read(struct input_stream *is, void *ptr, size_t size,
GError **error_r);
diff --git a/src/io_thread.c b/src/io_thread.c
new file mode 100644
index 000000000..fa6dee337
--- /dev/null
+++ b/src/io_thread.c
@@ -0,0 +1,184 @@
+/*
+ * 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.
+ */
+
+#include "io_thread.h"
+#include "glib_compat.h"
+
+#include <assert.h>
+
+static struct {
+ GMutex *mutex;
+ GCond *cond;
+
+ GMainContext *context;
+ GMainLoop *loop;
+ GThread *thread;
+} io;
+
+void
+io_thread_run(void)
+{
+ assert(io.context != NULL);
+ assert(io.loop != NULL);
+
+ g_main_loop_run(io.loop);
+}
+
+static gpointer
+io_thread_func(G_GNUC_UNUSED gpointer arg)
+{
+ io_thread_run();
+ return NULL;
+}
+
+void
+io_thread_init(void)
+{
+ assert(io.context == NULL);
+ assert(io.loop == NULL);
+ assert(io.thread == NULL);
+
+ io.mutex = g_mutex_new();
+ io.cond = g_cond_new();
+ io.context = g_main_context_new();
+ io.loop = g_main_loop_new(io.context, false);
+}
+
+bool
+io_thread_start(GError **error_r)
+{
+ assert(io.context != NULL);
+ assert(io.loop != NULL);
+ assert(io.thread == NULL);
+
+ io.thread = g_thread_create(io_thread_func, NULL, true, error_r);
+ if (io.thread == NULL)
+ return false;
+
+ return true;
+}
+
+void
+io_thread_quit(void)
+{
+ assert(io.loop != NULL);
+
+ g_main_loop_quit(io.loop);
+}
+
+void
+io_thread_deinit(void)
+{
+ if (io.thread != NULL) {
+ io_thread_quit();
+
+ g_thread_join(io.thread);
+ }
+
+ if (io.loop != NULL)
+ g_main_loop_unref(io.loop);
+
+ if (io.context != NULL)
+ g_main_context_unref(io.context);
+
+ g_cond_free(io.cond);
+ g_mutex_free(io.mutex);
+}
+
+GMainContext *
+io_thread_context(void)
+{
+ return io.context;
+}
+
+bool
+io_thread_inside(void)
+{
+ return io.thread != NULL && g_thread_self() == io.thread;
+}
+
+guint
+io_thread_idle_add(GSourceFunc function, gpointer data)
+{
+ GSource *source = g_idle_source_new();
+ g_source_set_callback(source, function, data, NULL);
+ guint id = g_source_attach(source, io.context);
+ g_source_unref(source);
+ return id;
+}
+
+guint
+io_thread_timeout_add_seconds(guint interval,
+ GSourceFunc function, gpointer data)
+{
+ GSource *source = g_timeout_source_new_seconds(interval);
+ g_source_set_callback(source, function, data, NULL);
+ guint id = g_source_attach(source, io.context);
+ g_source_unref(source);
+ return id;
+}
+
+struct call_data {
+ GThreadFunc function;
+ gpointer data;
+ bool done;
+ gpointer result;
+};
+
+static gboolean
+io_thread_call_func(gpointer _data)
+{
+ struct call_data *data = _data;
+
+ gpointer result = data->function(data->data);
+
+ g_mutex_lock(io.mutex);
+ data->done = true;
+ data->result = result;
+ g_cond_broadcast(io.cond);
+ g_mutex_unlock(io.mutex);
+
+ return false;
+}
+
+gpointer
+io_thread_call(GThreadFunc function, gpointer _data)
+{
+ assert(io.thread != NULL);
+
+ if (io_thread_inside())
+ /* we're already in the I/O thread - no
+ synchronization needed */
+ return function(_data);
+
+ struct call_data data = {
+ .function = function,
+ .data = _data,
+ .done = false,
+ };
+
+ io_thread_idle_add(io_thread_call_func, &data);
+
+ g_mutex_lock(io.mutex);
+ while (!data.done)
+ g_cond_wait(io.cond, io.mutex);
+ g_mutex_unlock(io.mutex);
+
+ return data.result;
+}
diff --git a/src/io_thread.h b/src/io_thread.h
new file mode 100644
index 000000000..c91597225
--- /dev/null
+++ b/src/io_thread.h
@@ -0,0 +1,75 @@
+/*
+ * 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_IO_THREAD_H
+#define MPD_IO_THREAD_H
+
+#include <glib.h>
+#include <stdbool.h>
+
+void
+io_thread_init(void);
+
+bool
+io_thread_start(GError **error_r);
+
+/**
+ * Run the I/O event loop synchronously in the current thread. This
+ * can be called instead of io_thread_start(). For testing purposes
+ * only.
+ */
+void
+io_thread_run(void);
+
+/**
+ * Ask the I/O thread to quit, but does not wait for it. Usually, you
+ * don't need to call this function, because io_thread_deinit()
+ * includes this.
+ */
+void
+io_thread_quit(void);
+
+void
+io_thread_deinit(void);
+
+G_GNUC_PURE
+GMainContext *
+io_thread_context(void);
+
+/**
+ * Is the current thread the I/O thread?
+ */
+G_GNUC_PURE
+bool
+io_thread_inside(void);
+
+guint
+io_thread_idle_add(GSourceFunc function, gpointer data);
+
+guint
+io_thread_timeout_add_seconds(guint interval,
+ GSourceFunc function, gpointer data);
+
+/**
+ * Call a function synchronously in the I/O thread.
+ */
+gpointer
+io_thread_call(GThreadFunc function, gpointer data);
+
+#endif
diff --git a/src/listen.c b/src/listen.c
index da2e79909..5c958507d 100644
--- a/src/listen.c
+++ b/src/listen.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
#include "client.h"
#include "conf.h"
#include "glib_compat.h"
+#include "main.h"
#include <string.h>
#include <assert.h>
@@ -39,7 +40,7 @@ static void
listen_callback(int fd, const struct sockaddr *address,
size_t address_length, int uid, G_GNUC_UNUSED void *ctx)
{
- client_new(fd, address, address_length, uid);
+ client_new(global_player_control, fd, address, address_length, uid);
}
static bool
diff --git a/src/listen.h b/src/listen.h
index 449b5ebae..246e83706 100644
--- a/src/listen.h
+++ b/src/listen.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/locate.c b/src/locate.c
index e27858a0e..96acb3a39 100644
--- a/src/locate.c
+++ b/src/locate.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -64,19 +64,6 @@ locate_item_init(struct locate_item *item,
return true;
}
-struct locate_item *
-locate_item_new(const char *type_string, const char *needle)
-{
- struct locate_item *ret = g_new(struct locate_item, 1);
-
- if (!locate_item_init(ret, type_string, needle)) {
- g_free(ret);
- ret = NULL;
- }
-
- return ret;
-}
-
void
locate_item_list_free(struct locate_item_list *list)
{
diff --git a/src/locate.h b/src/locate.h
index 0283f551b..ec20ded24 100644
--- a/src/locate.h
+++ b/src/locate.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,8 @@
#ifndef MPD_LOCATE_H
#define MPD_LOCATE_H
+#include "gcc.h"
+
#include <stdint.h>
#include <stdbool.h>
@@ -49,10 +51,6 @@ struct locate_item_list {
int
locate_parse_type(const char *str);
-/* returns NULL if not a known type */
-struct locate_item *
-locate_item_new(const char *type_string, const char *needle);
-
/**
* Allocates a new struct locate_item_list, and initializes all
* members with zero bytes.
@@ -61,6 +59,7 @@ struct locate_item_list *
locate_item_list_new(unsigned length);
/* return number of items or -1 on error */
+gcc_nonnull(1)
struct locate_item_list *
locate_item_list_parse(char *argv[], int argc);
@@ -68,19 +67,24 @@ locate_item_list_parse(char *argv[], int argc);
* Duplicate the struct locate_item_list object and convert all
* needles with g_utf8_casefold().
*/
+gcc_nonnull(1)
struct locate_item_list *
locate_item_list_casefold(const struct locate_item_list *list);
+gcc_nonnull(1)
void
locate_item_list_free(struct locate_item_list *list);
+gcc_nonnull(1)
void
locate_item_free(struct locate_item *item);
+gcc_nonnull(1,2)
bool
locate_song_search(const struct song *song,
const struct locate_item_list *criteria);
+gcc_nonnull(1,2)
bool
locate_song_match(const struct song *song,
const struct locate_item_list *criteria);
diff --git a/src/log.c b/src/log.c
index 556c8b04f..86dd86eaa 100644
--- a/src/log.c
+++ b/src/log.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -133,16 +133,20 @@ open_log_file(void)
return open_cloexec(out_filename, O_CREAT | O_WRONLY | O_APPEND, 0666);
}
-static void
-log_init_file(const char *path, unsigned line)
+static bool
+log_init_file(const char *path, unsigned line, GError **error_r)
{
out_filename = path;
out_fd = open_log_file();
- if (out_fd < 0)
- MPD_ERROR("problem opening log file \"%s\" (config line %u) "
- "for writing\n", path, line);
+ if (out_fd < 0) {
+ g_set_error(error_r, log_quark(), errno,
+ "failed to open log file \"%s\" (config line %u): %s",
+ path, line, g_strerror(errno));
+ return false;
+ }
g_log_set_default_handler(file_log_func, NULL);
+ return true;
}
#ifdef HAVE_SYSLOG
@@ -232,7 +236,8 @@ log_early_init(bool verbose)
log_init_stdout();
}
-void log_init(bool verbose, bool use_stdout)
+bool
+log_init(bool verbose, bool use_stdout, GError **error_r)
{
const struct config_param *param;
@@ -245,6 +250,7 @@ void log_init(bool verbose, bool use_stdout)
if (use_stdout) {
log_init_stdout();
+ return true;
} else {
param = config_get_param(CONF_LOG_FILE);
if (param == NULL) {
@@ -252,19 +258,31 @@ void log_init(bool verbose, bool use_stdout)
/* no configuration: default to syslog (if
available) */
log_init_syslog();
+ return true;
#else
- MPD_ERROR("config parameter \"%s\" not found\n",
- CONF_LOG_FILE);
+ g_set_error(error_r, log_quark(), 0,
+ "config parameter \"%s\" not found",
+ CONF_LOG_FILE);
+ return false;
#endif
#ifdef HAVE_SYSLOG
} else if (strcmp(param->value, "syslog") == 0) {
log_init_syslog();
+ return true;
#endif
} else {
- const char *path = config_get_path(CONF_LOG_FILE);
- assert(path != NULL);
-
- log_init_file(path, param->line);
+ GError *error = NULL;
+ char *path = config_dup_path(CONF_LOG_FILE, &error);
+ if (path == NULL) {
+ assert(error != NULL);
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ bool success = log_init_file(path, param->line,
+ error_r);
+ g_free(path);
+ return success;
}
}
}
diff --git a/src/log.h b/src/log.h
index e9daf1113..75e386b25 100644
--- a/src/log.h
+++ b/src/log.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,13 @@
#include <glib.h>
#include <stdbool.h>
+G_GNUC_CONST
+static inline GQuark
+log_quark(void)
+{
+ return g_quark_from_static_string("log");
+}
+
/**
* Configure a logging destination for daemon startup, before the
* configuration file is read. This allows the daemon to use the
@@ -34,7 +41,8 @@
void
log_early_init(bool verbose);
-void log_init(bool verbose, bool use_stdout);
+bool
+log_init(bool verbose, bool use_stdout, GError **error_r);
void setup_log_output(bool use_stdout);
diff --git a/src/ls.c b/src/ls.c
index c30765c62..5ed9fc579 100644
--- a/src/ls.c
+++ b/src/ls.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -32,7 +32,7 @@
* connected by IPC socket.
*/
static const char *remoteUrlPrefixes[] = {
-#ifdef ENABLE_CURL
+#if defined(ENABLE_CURL) || defined(ENABLE_SOUP)
"http://",
#endif
#ifdef ENABLE_MMS
@@ -49,6 +49,12 @@ static const char *remoteUrlPrefixes[] = {
"rtmpt://",
"rtmps://",
#endif
+#ifdef ENABLE_CDIO_PARANOIA
+ "cdda://",
+#endif
+#ifdef ENABLE_DESPOTIFY
+ "spt://",
+#endif
NULL
};
diff --git a/src/ls.h b/src/ls.h
index d29e20a46..15cb01160 100644
--- a/src/ls.h
+++ b/src/ls.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/main.c b/src/main.c
index a500e2934..49ce6e125 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,7 +20,9 @@
#include "config.h"
#include "main.h"
#include "daemon.h"
+#include "io_thread.h"
#include "client.h"
+#include "client_idle.h"
#include "idle.h"
#include "command.h"
#include "playlist.h"
@@ -94,31 +96,56 @@ GMainLoop *main_loop;
GCond *main_cond;
-static void
-glue_daemonize_init(const struct options *options)
+struct player_control *global_player_control;
+
+static bool
+glue_daemonize_init(const struct options *options, GError **error_r)
{
+ GError *error = NULL;
+
+ char *pid_file = config_dup_path(CONF_PID_FILE, &error);
+ if (pid_file == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
daemonize_init(config_get_string(CONF_USER, NULL),
config_get_string(CONF_GROUP, NULL),
- config_get_path(CONF_PID_FILE));
+ pid_file);
+ g_free(pid_file);
if (options->kill)
daemonize_kill();
+
+ return true;
}
-static void
-glue_mapper_init(void)
+static bool
+glue_mapper_init(GError **error_r)
{
- const char *music_dir, *playlist_dir;
+ GError *error = NULL;
+ char *music_dir = config_dup_path(CONF_MUSIC_DIR, &error);
+ if (music_dir == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ char *playlist_dir = config_dup_path(CONF_PLAYLIST_DIR, &error);
+ if (playlist_dir == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
- music_dir = config_get_path(CONF_MUSIC_DIR);
#if GLIB_CHECK_VERSION(2,14,0)
if (music_dir == NULL)
- music_dir = g_get_user_special_dir(G_USER_DIRECTORY_MUSIC);
+ music_dir = g_strdup(g_get_user_special_dir(G_USER_DIRECTORY_MUSIC));
#endif
- playlist_dir = config_get_path(CONF_PLAYLIST_DIR);
-
mapper_init(music_dir, playlist_dir);
+
+ g_free(music_dir);
+ g_free(playlist_dir);
+ return true;
}
/**
@@ -129,38 +156,31 @@ glue_mapper_init(void)
static bool
glue_db_init_and_load(void)
{
- const char *path = config_get_path(CONF_DB_FILE);
- bool ret;
+ const struct config_param *path = config_get_param(CONF_DB_FILE);
+
GError *error = NULL;
+ bool ret;
if (!mapper_has_music_directory()) {
if (path != NULL)
g_message("Found " CONF_DB_FILE " setting without "
CONF_MUSIC_DIR " - disabling database");
- db_init(NULL);
+ db_init(NULL, NULL);
return true;
}
if (path == NULL)
MPD_ERROR(CONF_DB_FILE " setting missing");
- db_init(path);
+ if (!db_init(path, &error))
+ MPD_ERROR("%s", error->message);
ret = db_load(&error);
- if (!ret) {
- g_warning("Failed to load database: %s", error->message);
- g_error_free(error);
-
- if (!db_check())
- exit(EXIT_FAILURE);
-
- db_clear();
-
- /* run database update after daemonization */
- return false;
- }
+ if (!ret)
+ MPD_ERROR("%s", error->message);
- return true;
+ /* run database update after daemonization? */
+ return db_exists();
}
/**
@@ -170,20 +190,34 @@ static void
glue_sticker_init(void)
{
#ifdef ENABLE_SQLITE
- bool success;
GError *error = NULL;
+ char *sticker_file = config_dup_path(CONF_STICKER_FILE, &error);
+ if (sticker_file == NULL && error != NULL)
+ MPD_ERROR("%s", error->message);
- success = sticker_global_init(config_get_path(CONF_STICKER_FILE),
- &error);
- if (!success)
+ if (!sticker_global_init(config_get_string(CONF_STICKER_FILE, NULL),
+ &error))
MPD_ERROR("%s", error->message);
+
+ g_free(sticker_file);
#endif
}
-static void
-glue_state_file_init(void)
+static bool
+glue_state_file_init(GError **error_r)
{
- state_file_init(config_get_path(CONF_STATE_FILE));
+ GError *error = NULL;
+
+ char *path = config_dup_path(CONF_STATE_FILE, &error);
+ if (path == NULL && error != NULL) {
+ g_propagate_error(error_r, error);
+ return false;
+ }
+
+ state_file_init(path, global_player_control);
+ g_free(path);
+
+ return true;
}
/**
@@ -254,7 +288,7 @@ initialize_decoder_and_player(void)
if (buffered_before_play > buffered_chunks)
buffered_before_play = buffered_chunks;
- pc_init(buffered_chunks, buffered_before_play);
+ global_player_control = pc_new(buffered_chunks, buffered_before_play);
}
/**
@@ -308,6 +342,7 @@ int mpd_main(int argc, char *argv[])
/* enable GLib's thread safety code */
g_thread_init(NULL);
+ io_thread_init();
winsock_init();
idle_init();
dirvec_init();
@@ -322,11 +357,20 @@ int mpd_main(int argc, char *argv[])
return EXIT_FAILURE;
}
- glue_daemonize_init(&options);
+ if (!glue_daemonize_init(&options, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
stats_global_init();
tag_lib_init();
- log_init(options.verbose, options.log_stderr);
+
+ if (!log_init(options.verbose, options.log_stderr, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
success = listen_global_init(&error);
if (!success) {
@@ -346,7 +390,13 @@ int mpd_main(int argc, char *argv[])
event_pipe_register(PIPE_EVENT_SHUTDOWN, shutdown_event_emitted);
path_global_init();
- glue_mapper_init();
+
+ if (!glue_mapper_init(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
initPermissions();
playlist_global_init();
spl_global_init();
@@ -364,7 +414,7 @@ int mpd_main(int argc, char *argv[])
initialize_decoder_and_player();
volume_init();
initAudioConfig();
- audio_output_all_init();
+ audio_output_all_init(global_player_control);
client_manager_init();
replay_gain_global_init();
@@ -382,9 +432,15 @@ int mpd_main(int argc, char *argv[])
initSigHandlers();
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
initZeroconf();
- player_create();
+ player_create(global_player_control);
if (create_db) {
/* the database failed to load: recreate the
@@ -394,7 +450,11 @@ int mpd_main(int argc, char *argv[])
MPD_ERROR("directory update failed");
}
- glue_state_file_init();
+ if (!glue_state_file_init(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
success = config_get_bool(CONF_AUTO_UPDATE, false);
#ifdef ENABLE_INOTIFY
@@ -410,7 +470,7 @@ int mpd_main(int argc, char *argv[])
/* enable all audio outputs (if not already done by
playlist_state_restore() */
- pc_update_audio();
+ pc_update_audio(global_player_control);
#ifdef WIN32
win32_app_started();
@@ -431,8 +491,8 @@ int mpd_main(int argc, char *argv[])
mpd_inotify_finish();
#endif
- state_file_finish();
- pc_kill();
+ state_file_finish(global_player_control);
+ pc_kill(global_player_control);
finishZeroconf();
client_manager_deinit();
listen_global_finish();
@@ -457,7 +517,7 @@ int mpd_main(int argc, char *argv[])
mapper_finish();
path_global_finish();
finishPermissions();
- pc_deinit();
+ pc_free(global_player_control);
command_finish();
update_global_finish();
decoder_plugin_deinit_all();
@@ -470,6 +530,7 @@ int mpd_main(int argc, char *argv[])
dirvec_deinit();
idle_deinit();
stats_global_finish();
+ io_thread_deinit();
daemonize_finish();
#ifdef WIN32
WSACleanup();
diff --git a/src/main.h b/src/main.h
index 9b9cba018..2a7d75910 100644
--- a/src/main.h
+++ b/src/main.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,8 @@ extern GMainLoop *main_loop;
extern GCond *main_cond;
+extern struct player_control *global_player_control;
+
/**
* A entry point for application.
* On non-Windows platforms this is called directly from main()
diff --git a/src/main_win32.c b/src/main_win32.c
index 543d8ba81..edac8a8dc 100644
--- a/src/main_win32.c
+++ b/src/main_win32.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mapper.c b/src/mapper.c
index 108de9531..091db50b0 100644
--- a/src/mapper.c
+++ b/src/mapper.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mapper.h b/src/mapper.h
index 9f84f96fe..8249a229f 100644
--- a/src/mapper.h
+++ b/src/mapper.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer/alsa_mixer_plugin.c b/src/mixer/alsa_mixer_plugin.c
index 38f36cb8f..756ae3ee5 100644
--- a/src/mixer/alsa_mixer_plugin.c
+++ b/src/mixer/alsa_mixer_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer/oss_mixer_plugin.c b/src/mixer/oss_mixer_plugin.c
index 418068ac2..608f1f9b8 100644
--- a/src/mixer/oss_mixer_plugin.c
+++ b/src/mixer/oss_mixer_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer/pulse_mixer_plugin.c b/src/mixer/pulse_mixer_plugin.c
index 2be0b8266..8fbba4c5a 100644
--- a/src/mixer/pulse_mixer_plugin.c
+++ b/src/mixer/pulse_mixer_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer/pulse_mixer_plugin.h b/src/mixer/pulse_mixer_plugin.h
index be199f688..461633d37 100644
--- a/src/mixer/pulse_mixer_plugin.h
+++ b/src/mixer/pulse_mixer_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer/raop_mixer_plugin.c b/src/mixer/raop_mixer_plugin.c
new file mode 100644
index 000000000..b05671212
--- /dev/null
+++ b/src/mixer/raop_mixer_plugin.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2003-2009 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 "../output/raop_output_plugin.h"
+#include "output_plugin.h"
+#include "mixer_api.h"
+
+struct raop_mixer_plugin {
+ struct mixer base;
+ struct raop_data *rd;
+};
+
+static struct mixer *
+raop_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param,
+ G_GNUC_UNUSED GError **error_r)
+{
+ struct raop_mixer_plugin *rm = g_new(struct raop_mixer_plugin, 1);
+ rm->rd = (struct raop_data *) ao;
+ mixer_init(&rm->base, &raop_mixer_plugin);
+
+ return &rm->base;
+}
+
+static void
+raop_mixer_finish(struct mixer *data)
+{
+ struct raop_mixer_plugin *rm = (struct raop_mixer_plugin *) data;
+
+ g_free(rm);
+}
+
+static int
+raop_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r)
+{
+ struct raop_mixer_plugin *rm = (struct raop_mixer_plugin *)mixer;
+ return raop_get_volume(rm->rd);
+}
+
+static bool
+raop_mixer_set_volume(struct mixer *mixer, unsigned volume, GError **error_r)
+{
+ struct raop_mixer_plugin *rm = (struct raop_mixer_plugin *)mixer;
+ return raop_set_volume(rm->rd, volume, error_r);
+}
+
+const struct mixer_plugin raop_mixer_plugin = {
+ .init = raop_mixer_init,
+ .finish = raop_mixer_finish,
+ .get_volume = raop_mixer_get_volume,
+ .set_volume = raop_mixer_set_volume,
+};
diff --git a/src/mixer/roar_mixer_plugin.c b/src/mixer/roar_mixer_plugin.c
new file mode 100644
index 000000000..636a9c00e
--- /dev/null
+++ b/src/mixer/roar_mixer_plugin.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
+ * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
+ *
+ * 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 "mixer_api.h"
+#include "output_api.h"
+#include "output/roar_output_plugin.h"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+typedef struct roar_mpd_mixer
+{
+ /** the base mixer class */
+ struct mixer base;
+ roar_t *self;
+} roar_mixer_t;
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+roar_mixer_quark(void)
+{
+ return g_quark_from_static_string("roar_mixer");
+}
+
+static struct mixer *
+roar_mixer_init(void *ao, G_GNUC_UNUSED const struct config_param *param,
+ G_GNUC_UNUSED GError **error_r)
+{
+ roar_mixer_t *self = g_new(roar_mixer_t, 1);
+ self->self = ao;
+
+ mixer_init(&self->base, &roar_mixer_plugin);
+
+ return &self->base;
+}
+
+static void
+roar_mixer_finish(struct mixer *data)
+{
+ roar_mixer_t *self = (roar_mixer_t *) data;
+
+ g_free(self);
+}
+
+static void
+roar_mixer_close(G_GNUC_UNUSED struct mixer *data)
+{
+}
+
+static bool
+roar_mixer_open(G_GNUC_UNUSED struct mixer *data,
+ G_GNUC_UNUSED GError **error_r)
+{
+ return true;
+}
+
+static int
+roar_mixer_get_volume(struct mixer *mixer, G_GNUC_UNUSED GError **error_r)
+{
+ roar_mixer_t *self = (roar_mixer_t *)mixer;
+ g_mutex_lock(self->self->lock);
+ if (self->self->vss && self->self->alive)
+ {
+ float l, r;
+ int error;
+ roar_vs_volume_get(self->self->vss, &l, &r, &error);
+ g_mutex_unlock(self->self->lock);
+ return (l + r) * 50;
+ }
+ else
+ {
+ g_mutex_unlock(self->self->lock);
+ return 0;
+ }
+}
+
+static bool
+roar_mixer_set_volume(struct mixer *mixer, unsigned volume,
+ G_GNUC_UNUSED GError **error_r)
+{
+ roar_mixer_t *self = (roar_mixer_t *)mixer;
+ g_mutex_lock(self->self->lock);
+ if (self->self->vss && self->self->alive)
+ {
+ assert(volume <= 100);
+
+ int error;
+ float level = volume / 100.0;
+
+ roar_vs_volume_mono(self->self->vss, level, &error);
+ g_mutex_unlock(self->self->lock);
+ return true;
+ }
+ else
+ {
+ g_mutex_unlock(self->self->lock);
+ return false;
+ }
+}
+
+const struct mixer_plugin roar_mixer_plugin = {
+ .init = roar_mixer_init,
+ .finish = roar_mixer_finish,
+ .open = roar_mixer_open,
+ .close = roar_mixer_close,
+ .get_volume = roar_mixer_get_volume,
+ .set_volume = roar_mixer_set_volume,
+ .global = false,
+};
diff --git a/src/mixer/software_mixer_plugin.c b/src/mixer/software_mixer_plugin.c
index 93802e977..0206c3b99 100644
--- a/src/mixer/software_mixer_plugin.c
+++ b/src/mixer/software_mixer_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer/software_mixer_plugin.h b/src/mixer/software_mixer_plugin.h
index 3bd07ac62..ee2b2023c 100644
--- a/src/mixer/software_mixer_plugin.h
+++ b/src/mixer/software_mixer_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer/winmm_mixer_plugin.c b/src/mixer/winmm_mixer_plugin.c
index 5ab3e7525..ceddf6afd 100644
--- a/src/mixer/winmm_mixer_plugin.c
+++ b/src/mixer/winmm_mixer_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_all.c b/src/mixer_all.c
index ffe610b91..95ba90793 100644
--- a/src/mixer_all.c
+++ b/src/mixer_all.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_all.h b/src/mixer_all.h
index cece23292..fe873e713 100644
--- a/src/mixer_all.h
+++ b/src/mixer_all.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_api.c b/src/mixer_api.c
index 4c8959fb8..c85916c94 100644
--- a/src/mixer_api.c
+++ b/src/mixer_api.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_api.h b/src/mixer_api.h
index 26c001703..29c1e00ca 100644
--- a/src/mixer_api.h
+++ b/src/mixer_api.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_control.c b/src/mixer_control.c
index 458b3abc1..3e984dd04 100644
--- a/src/mixer_control.c
+++ b/src/mixer_control.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_control.h b/src/mixer_control.h
index 1f48e8ca5..6c3468aca 100644
--- a/src/mixer_control.h
+++ b/src/mixer_control.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_list.h b/src/mixer_list.h
index a472c8807..95ded5c23 100644
--- a/src/mixer_list.h
+++ b/src/mixer_list.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -28,7 +28,9 @@
extern const struct mixer_plugin software_mixer_plugin;
extern const struct mixer_plugin alsa_mixer_plugin;
extern const struct mixer_plugin oss_mixer_plugin;
+extern const struct mixer_plugin roar_mixer_plugin;
extern const struct mixer_plugin pulse_mixer_plugin;
+extern const struct mixer_plugin raop_mixer_plugin;
extern const struct mixer_plugin winmm_mixer_plugin;
#endif
diff --git a/src/mixer_plugin.h b/src/mixer_plugin.h
index 0915a03f3..9532b95cb 100644
--- a/src/mixer_plugin.h
+++ b/src/mixer_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -41,7 +41,7 @@ struct mixer_plugin {
* @param ao the pointer returned by audio_output_plugin.init
* @param param the configuration section, or NULL if there is
* no configuration
- * @param error_r location to store the error occuring, or
+ * @param error_r location to store the error occurring, or
* NULL to ignore errors
* @return a mixer object, or NULL on error
*/
@@ -56,7 +56,7 @@ struct mixer_plugin {
/**
* Open mixer device
*
- * @param error_r location to store the error occuring, or
+ * @param error_r location to store the error occurring, or
* NULL to ignore errors
* @return true on success, false on error
*/
@@ -70,7 +70,7 @@ struct mixer_plugin {
/**
* Reads the current volume.
*
- * @param error_r location to store the error occuring, or
+ * @param error_r location to store the error occurring, or
* NULL to ignore errors
* @return the current volume (0..100 including) or -1 if
* unavailable or on error (error_r set, mixer will be closed)
@@ -80,7 +80,7 @@ struct mixer_plugin {
/**
* Sets the volume.
*
- * @param error_r location to store the error occuring, or
+ * @param error_r location to store the error occurring, or
* NULL to ignore errors
* @param volume the new volume (0..100 including)
* @return true on success, false on error
diff --git a/src/mixer_type.c b/src/mixer_type.c
index 4f347dd94..a479caf16 100644
--- a/src/mixer_type.c
+++ b/src/mixer_type.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mixer_type.h b/src/mixer_type.h
index fd1c5576c..15d136b5b 100644
--- a/src/mixer_type.h
+++ b/src/mixer_type.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/mpd_error.h b/src/mpd_error.h
index 47618d03c..219738ced 100644
--- a/src/mpd_error.h
+++ b/src/mpd_error.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/notify.c b/src/notify.c
index d148a4bfc..3c0112c91 100644
--- a/src/notify.c
+++ b/src/notify.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/notify.h b/src/notify.h
index 0c657f2fb..40821690c 100644
--- a/src/notify.h
+++ b/src/notify.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/ntp_server.c b/src/ntp_server.c
new file mode 100644
index 000000000..c33c0a933
--- /dev/null
+++ b/src/ntp_server.c
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#include "ntp_server.h"
+#include "udp_server.h"
+
+#include <glib.h>
+#include <assert.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#ifdef WIN32
+#define WINVER 0x0501
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#endif
+
+/*
+ * Calculate the current NTP time, store it in the buffer.
+ */
+static void
+fill_int(unsigned char *buffer, uint32_t value)
+{
+ uint32_t be = GINT32_TO_BE(value);
+ memcpy(buffer, &be, sizeof(be));
+}
+
+/*
+ * Store time in the NTP format in the buffer
+ */
+static void
+fill_time_buffer_with_time(unsigned char *buffer, struct timeval *tout)
+{
+ unsigned long secs_to_baseline = 964697997;
+ double fraction;
+ unsigned long long_fraction;
+ unsigned long secs;
+
+ fraction = ((double) tout->tv_usec) / 1000000.0;
+ long_fraction = (unsigned long) (fraction * 256.0 * 256.0 * 256.0 * 256.0);
+ secs = secs_to_baseline + tout->tv_sec;
+ fill_int(buffer, secs);
+ fill_int(buffer + 4, long_fraction);
+}
+
+/*
+ * Calculate the current NTP time, store it in the buffer.
+ */
+static void
+fill_time_buffer(unsigned char *buffer)
+{
+ struct timeval current_time;
+
+ gettimeofday(&current_time,NULL);
+ fill_time_buffer_with_time(buffer, &current_time);
+}
+
+static void
+ntp_server_datagram(int fd, const void *data, size_t num_bytes,
+ const struct sockaddr *source_address,
+ size_t source_address_length, G_GNUC_UNUSED void *ctx)
+{
+ unsigned char buf[32];
+ int iter;
+
+ if (num_bytes > sizeof(buf))
+ num_bytes = sizeof(buf);
+ memcpy(buf, data, num_bytes);
+
+ fill_time_buffer(buf + 16);
+ // set to response
+ buf[1] = 0xd3;
+ // copy request
+ for (iter = 0; iter < 8; iter++) {
+ buf[8 + iter] = buf[24 + iter];
+ }
+ fill_time_buffer(buf + 24);
+
+ sendto(fd, (void *)buf, num_bytes, 0,
+ source_address, source_address_length);
+}
+
+static const struct udp_server_handler ntp_server_handler = {
+ .datagram = ntp_server_datagram,
+};
+
+void
+ntp_server_init(struct ntp_server *ntp)
+{
+ ntp->port = 6002;
+ ntp->udp = NULL;
+}
+
+bool
+ntp_server_open(struct ntp_server *ntp, GError **error_r)
+{
+ assert(ntp->udp == NULL);
+
+ ntp->udp = udp_server_new(ntp->port, &ntp_server_handler, ntp,
+ error_r);
+ return ntp->udp != NULL;
+}
+
+void
+ntp_server_close(struct ntp_server *ntp)
+{
+ if (ntp->udp != NULL) {
+ udp_server_free(ntp->udp);
+ ntp->udp = NULL;
+ }
+}
diff --git a/src/ntp_server.h b/src/ntp_server.h
new file mode 100644
index 000000000..fe6f8083b
--- /dev/null
+++ b/src/ntp_server.h
@@ -0,0 +1,44 @@
+/*
+ * 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_NTP_SERVER_H
+#define MPD_NTP_SERVER_H
+
+#include <glib.h>
+
+#include <stdbool.h>
+
+struct timeval;
+
+struct ntp_server {
+ unsigned short port;
+
+ struct udp_server *udp;
+};
+
+void
+ntp_server_init(struct ntp_server *ntp);
+
+bool
+ntp_server_open(struct ntp_server *ntp, GError **error_r);
+
+void
+ntp_server_close(struct ntp_server *ntp);
+
+#endif
diff --git a/src/open.h b/src/open.h
index e39c64a97..1fe245dfa 100644
--- a/src/open.h
+++ b/src/open.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/alsa_plugin.c b/src/output/alsa_plugin.c
index 422264f53..0bbe231fd 100644
--- a/src/output/alsa_plugin.c
+++ b/src/output/alsa_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/ao_plugin.c b/src/output/ao_plugin.c
index 42ece5a3a..33366d3b8 100644
--- a/src/output/ao_plugin.c
+++ b/src/output/ao_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/ffado_output_plugin.c b/src/output/ffado_output_plugin.c
index 723698ed0..1a4ec3919 100644
--- a/src/output/ffado_output_plugin.c
+++ b/src/output/ffado_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/fifo_output_plugin.c b/src/output/fifo_output_plugin.c
index f4217ec4d..708062552 100644
--- a/src/output/fifo_output_plugin.c
+++ b/src/output/fifo_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -42,7 +42,7 @@ struct fifo_data {
int input;
int output;
bool created;
- Timer *timer;
+ struct timer *timer;
};
/**
@@ -178,30 +178,25 @@ fifo_open(struct fifo_data *fd, GError **error)
static void *
fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
const struct config_param *param,
- GError **error)
+ GError **error_r)
{
struct fifo_data *fd;
- char *value, *path;
-
- value = config_dup_block_string(param, "path", NULL);
- if (value == NULL) {
- g_set_error(error, fifo_output_quark(), errno,
- "No \"path\" parameter specified");
- return NULL;
- }
- path = parsePath(value);
- g_free(value);
+ GError *error = NULL;
+ char *path = config_dup_block_path(param, "path", &error);
if (!path) {
- g_set_error(error, fifo_output_quark(), errno,
- "Could not parse \"path\" parameter");
+ if (error != NULL)
+ g_propagate_error(error_r, error);
+ else
+ g_set_error(error_r, fifo_output_quark(), 0,
+ "No \"path\" parameter specified");
return NULL;
}
fd = fifo_data_new();
fd->path = path;
- if (!fifo_open(fd, error)) {
+ if (!fifo_open(fd, error_r)) {
fifo_data_free(fd);
return NULL;
}
diff --git a/src/output/httpd_client.c b/src/output/httpd_client.c
index 1119a7834..e2c49c6c8 100644
--- a/src/output/httpd_client.c
+++ b/src/output/httpd_client.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/httpd_client.h b/src/output/httpd_client.h
index 7ebd0bbc0..739163f42 100644
--- a/src/output/httpd_client.h
+++ b/src/output/httpd_client.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/httpd_internal.h b/src/output/httpd_internal.h
index 277e70f11..3e6e9768d 100644
--- a/src/output/httpd_internal.h
+++ b/src/output/httpd_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -65,10 +65,10 @@ struct httpd_output {
GMutex *mutex;
/**
- * A #Timer object to synchronize this output with the
+ * A #timer object to synchronize this output with the
* wallclock.
*/
- Timer *timer;
+ struct timer *timer;
/**
* The listener socket.
diff --git a/src/output/httpd_output_plugin.c b/src/output/httpd_output_plugin.c
index 40ad05c3d..3f570c7b9 100644
--- a/src/output/httpd_output_plugin.c
+++ b/src/output/httpd_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/jack_output_plugin.c b/src/output/jack_output_plugin.c
index 2767d4eb8..4df84fd23 100644
--- a/src/output/jack_output_plugin.c
+++ b/src/output/jack_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/mvp_plugin.c b/src/output/mvp_plugin.c
index 6cc8fa34e..be4c8dbc0 100644
--- a/src/output/mvp_plugin.c
+++ b/src/output/mvp_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/null_plugin.c b/src/output/null_plugin.c
index 89abbd91f..f572959a0 100644
--- a/src/output/null_plugin.c
+++ b/src/output/null_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -28,7 +28,7 @@
struct null_data {
bool sync;
- Timer *timer;
+ struct timer *timer;
};
static void *
@@ -82,7 +82,7 @@ null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size,
G_GNUC_UNUSED GError **error)
{
struct null_data *nd = data;
- Timer *timer = nd->timer;
+ struct timer *timer = nd->timer;
if (!nd->sync)
return size;
diff --git a/src/output/openal_plugin.c b/src/output/openal_plugin.c
index 767b3eb17..23641c6c5 100644
--- a/src/output/openal_plugin.c
+++ b/src/output/openal_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -41,7 +41,7 @@ struct openal_data {
const char *device_name;
ALCdevice *device;
ALCcontext *context;
- Timer *timer;
+ struct timer *timer;
ALuint buffers[NUM_BUFFERS];
int filled;
ALuint source;
diff --git a/src/output/oss_plugin.c b/src/output/oss_plugin.c
index 9261b423c..d7df594d3 100644
--- a/src/output/oss_plugin.c
+++ b/src/output/oss_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/osx_plugin.c b/src/output/osx_plugin.c
index 7639f3bd9..8091660ab 100644
--- a/src/output/osx_plugin.c
+++ b/src/output/osx_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -28,6 +28,11 @@
#define G_LOG_DOMAIN "osx"
struct osx_output {
+ /* configuration settings */
+ OSType component_subtype;
+ /* only applicable with kAudioUnitSubType_HALOutput */
+ const char *device_name;
+
AudioUnit au;
GMutex *mutex;
GCond *condition;
@@ -54,6 +59,26 @@ osx_output_test_default_device(void)
return true;
}
+static void
+osx_output_configure(struct osx_output *oo, const struct config_param *param)
+{
+ const char *device = config_get_block_string(param, "device", NULL);
+
+ if (device == NULL || 0 == strcmp(device, "default")) {
+ oo->component_subtype = kAudioUnitSubType_DefaultOutput;
+ oo->device_name = NULL;
+ }
+ else if (0 == strcmp(device, "system")) {
+ oo->component_subtype = kAudioUnitSubType_SystemOutput;
+ oo->device_name = NULL;
+ }
+ else {
+ oo->component_subtype = kAudioUnitSubType_HALOutput;
+ /* XXX am I supposed to g_strdup() this? */
+ oo->device_name = device;
+ }
+}
+
static void *
osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
G_GNUC_UNUSED const struct config_param *param,
@@ -61,6 +86,7 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
{
struct osx_output *oo = g_new(struct osx_output, 1);
+ osx_output_configure(oo, param);
oo->mutex = g_mutex_new();
oo->condition = g_cond_new();
@@ -150,6 +176,95 @@ osx_render(void *vdata,
}
static bool
+osx_output_set_device(struct osx_output *oo, GError **error)
+{
+ bool ret = true;
+ OSStatus status;
+ UInt32 size, numdevices;
+ AudioDeviceID *deviceids = NULL;
+ char name[256];
+ unsigned int i;
+
+ if (oo->component_subtype != kAudioUnitSubType_HALOutput)
+ goto done;
+
+ /* how many audio devices are there? */
+ status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &size,
+ NULL);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine number of OS X audio devices: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+
+ /* what are the available audio device IDs? */
+ numdevices = size / sizeof(AudioDeviceID);
+ deviceids = g_malloc(size);
+ status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &size,
+ deviceids);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine OS X audio device IDs: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+
+ /* which audio device matches oo->device_name? */
+ for (i = 0; i < numdevices; i++) {
+ size = sizeof(name);
+ status = AudioDeviceGetProperty(deviceids[i], 0, false,
+ kAudioDevicePropertyDeviceName,
+ &size, name);
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to determine OS X device name "
+ "(device %u): %s",
+ (unsigned int) deviceids[i],
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+ if (strcmp(oo->device_name, name) == 0) {
+ g_debug("found matching device: ID=%u, name=%s",
+ (unsigned int) deviceids[i], name);
+ break;
+ }
+ }
+ if (i == numdevices) {
+ g_warning("Found no audio device with name '%s' "
+ "(will use default audio device)",
+ oo->device_name);
+ goto done;
+ }
+
+ status = AudioUnitSetProperty(oo->au,
+ kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ 0,
+ &(deviceids[i]),
+ sizeof(AudioDeviceID));
+ if (status != noErr) {
+ g_set_error(error, osx_output_quark(), status,
+ "Unable to set OS X audio output device: %s",
+ GetMacOSStatusCommentString(status));
+ ret = false;
+ goto done;
+ }
+ g_debug("set OS X audio output device ID=%u, name=%s",
+ (unsigned int) deviceids[i], name);
+
+done:
+ if (deviceids != NULL)
+ g_free(deviceids);
+ return ret;
+}
+
+static bool
osx_output_open(void *data, struct audio_format *audio_format, GError **error)
{
struct osx_output *od = data;
@@ -161,7 +276,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
ComponentResult result;
desc.componentType = kAudioUnitType_Output;
- desc.componentSubType = kAudioUnitSubType_DefaultOutput;
+ desc.componentSubType = od->component_subtype;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
desc.componentFlags = 0;
desc.componentFlagsMask = 0;
@@ -175,7 +290,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
status = OpenAComponent(comp, &od->au);
if (status != noErr) {
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), status,
"Unable to open OS X component: %s",
GetMacOSStatusCommentString(status));
return false;
@@ -184,12 +299,15 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
status = AudioUnitInitialize(od->au);
if (status != noErr) {
CloseComponent(od->au);
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), status,
"Unable to initialize OS X audio unit: %s",
GetMacOSStatusCommentString(status));
return false;
}
+ if (!osx_output_set_device(od, error))
+ return false;
+
callback.inputProc = osx_render;
callback.inputProcRefCon = od;
@@ -200,7 +318,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
if (result != noErr) {
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), result,
"unable to set callback for OS X audio unit");
return false;
}
@@ -241,7 +359,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
if (result != noErr) {
AudioUnitUninitialize(od->au);
CloseComponent(od->au);
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), result,
"Unable to set format on OS X device");
return false;
}
@@ -256,7 +374,7 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error)
status = AudioOutputUnitStart(od->au);
if (status != 0) {
- g_set_error(error, osx_output_quark(), 0,
+ g_set_error(error, osx_output_quark(), status,
"unable to start audio output: %s",
GetMacOSStatusCommentString(status));
return false;
diff --git a/src/output/pipe_output_plugin.c b/src/output/pipe_output_plugin.c
index 1d1aec7b1..111c654b9 100644
--- a/src/output/pipe_output_plugin.c
+++ b/src/output/pipe_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/pulse_output_plugin.c b/src/output/pulse_output_plugin.c
index 911835e46..ecd6a487a 100644
--- a/src/output/pulse_output_plugin.c
+++ b/src/output/pulse_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/pulse_output_plugin.h b/src/output/pulse_output_plugin.h
index 06e3aec43..2261175d5 100644
--- a/src/output/pulse_output_plugin.h
+++ b/src/output/pulse_output_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/raop_output_plugin.c b/src/output/raop_output_plugin.c
new file mode 100644
index 000000000..735aea976
--- /dev/null
+++ b/src/output/raop_output_plugin.c
@@ -0,0 +1,950 @@
+/*
+ * 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.
+ */
+
+#include "raop_output_plugin.h"
+#include "output_api.h"
+#include "mixer_list.h"
+#include "raop_output_plugin.h"
+#include "rtsp_client.h"
+#include "glib_compat.h"
+
+#include <glib.h>
+#include <unistd.h>
+#include <openssl/err.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/engine.h>
+
+#ifndef WIN32
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "raop"
+
+static struct raop_session_data *raop_session = NULL;
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+raop_output_quark(void)
+{
+ return g_quark_from_static_string("raop_output");
+}
+
+static void
+raop_session_free(struct raop_session_data *session)
+{
+ assert(session != NULL);
+ assert(session->raop_list == NULL);
+
+ ntp_server_close(&session->ntp);
+
+ if (session->data_mutex != NULL)
+ g_mutex_free(session->data_mutex);
+
+ if (session->list_mutex != NULL)
+ g_mutex_free(session->list_mutex);
+
+ if (raop_session->data_fd >= 0)
+ close(raop_session->data_fd);
+
+ if (raop_session->ctrl.fd >= 0)
+ close(raop_session->ctrl.fd);
+
+ g_free(session);
+}
+
+static struct raop_session_data *
+raop_session_new(GError **error_r)
+{
+ struct raop_session_data *session = g_new(struct raop_session_data, 1);
+ session->raop_list = NULL;
+
+ session->data_mutex = g_mutex_new();
+ session->list_mutex = g_mutex_new();
+
+ ntp_server_init(&session->ntp);
+ session->ctrl.port = 6001;
+ session->ctrl.fd = -1;
+ session->play_state.playing = false;
+ session->play_state.seq_num = (short) g_random_int();
+ session->play_state.rtptime = g_random_int();
+ session->play_state.sync_src = g_random_int();
+ session->play_state.last_send.tv_sec = 0;
+ session->play_state.last_send.tv_usec = 0;
+ session->data_fd = -1;
+
+ if (!RAND_bytes(session->encrypt.iv, sizeof(session->encrypt.iv)) ||
+ !RAND_bytes(session->encrypt.key, sizeof(session->encrypt.key))) {
+ raop_session_free(session);
+ g_set_error(error_r, raop_output_quark(), 0,
+ "RAND_bytes error code=%ld", ERR_get_error());
+ return NULL;
+ }
+ memcpy(session->encrypt.nv, session->encrypt.iv, sizeof(session->encrypt.nv));
+ for (unsigned i = 0; i < 16; i++) {
+ printf("0x%x ", session->encrypt.key[i]);
+ }
+ printf("\n");
+ AES_set_encrypt_key(session->encrypt.key, 128, &session->encrypt.ctx);
+
+ memset(session->buffer, 0, RAOP_BUFFER_SIZE);
+ session->bufferSize = 0;
+
+ return session;
+}
+
+static struct raop_data *
+new_raop_data(GError **error_r)
+{
+ struct raop_data *ret = g_new(struct raop_data, 1);
+
+ ret->control_mutex = g_mutex_new();
+
+ ret->next = NULL;
+ ret->is_master = 0;
+ ret->started = 0;
+ ret->paused = 0;
+
+ if (raop_session == NULL &&
+ (raop_session = raop_session_new(error_r)) == NULL) {
+ g_mutex_free(ret->control_mutex);
+ g_free(ret);
+ return NULL;
+ }
+
+ return ret;
+}
+
+/*
+ * remove one character from a string
+ * return the number of deleted characters
+ */
+static int
+remove_char_from_string(char *str, char c)
+{
+ char *src, *dst;
+
+ /* skip all characters that don't need to be copied */
+ src = strchr(str, c);
+ if (!src)
+ return 0;
+
+ for (dst = src; *src; src++)
+ if (*src != c)
+ *(dst++) = *src;
+
+ *dst = '\0';
+
+ return src - dst;
+}
+
+/* bind an opened socket to specified hostname and port.
+ * if hostname=NULL, use INADDR_ANY.
+ * if *port=0, use dynamically assigned port
+ */
+static int bind_host(int sd, char *hostname, unsigned long ulAddr,
+ unsigned short *port, GError **error_r)
+{
+ struct sockaddr_in my_addr;
+ socklen_t nlen = sizeof(struct sockaddr);
+ struct hostent *h;
+
+ memset(&my_addr, 0, sizeof(my_addr));
+ /* use specified hostname */
+ if (hostname) {
+ /* get server IP address (no check if input is IP address or DNS name) */
+ h = gethostbyname(hostname);
+ if (h == NULL) {
+ if (strstr(hostname, "255.255.255.255") == hostname) {
+ my_addr.sin_addr.s_addr=-1;
+ } else {
+ if ((my_addr.sin_addr.s_addr = inet_addr(hostname)) == 0xFFFFFFFF) {
+ g_set_error(error_r, raop_output_quark(), 0,
+ "failed to resolve host '%s'",
+ hostname);
+ return -1;
+ }
+ }
+ my_addr.sin_family = AF_INET;
+ } else {
+ my_addr.sin_family = h->h_addrtype;
+ memcpy((char *) &my_addr.sin_addr.s_addr,
+ h->h_addr_list[0], h->h_length);
+ }
+ } else {
+ // if hostname=NULL, use INADDR_ANY
+ if (ulAddr)
+ my_addr.sin_addr.s_addr = ulAddr;
+ else
+ my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ my_addr.sin_family = AF_INET;
+ }
+
+ /* bind a specified port */
+ my_addr.sin_port = htons(*port);
+
+ if (bind(sd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
+ g_set_error(error_r, raop_output_quark(), errno,
+ "failed to bind socket: %s",
+ g_strerror(errno));
+ return -1;
+ }
+
+ if (*port == 0) {
+ getsockname(sd, (struct sockaddr *) &my_addr, &nlen);
+ *port = ntohs(my_addr.sin_port);
+ }
+
+ return 0;
+}
+
+/*
+ * open udp port
+ */
+static int
+open_udp_socket(char *hostname, unsigned short *port,
+ GError **error_r)
+{
+ int sd;
+ int size = 30000;
+
+ /* socket creation */
+ sd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (sd < 0) {
+ g_set_error(error_r, raop_output_quark(), errno,
+ "failed to create UDP socket: %s",
+ g_strerror(errno));
+ return -1;
+ }
+ if (setsockopt(sd, SOL_SOCKET, SO_SNDBUF, (void *) &size, sizeof(size)) < 0) {
+ g_set_error(error_r, raop_output_quark(), errno,
+ "failed to set UDP buffer size: %s",
+ g_strerror(errno));
+ return -1;
+ }
+ if (bind_host(sd, hostname, 0, port, error_r)) {
+ close(sd);
+ return -1;
+ }
+
+ return sd;
+}
+
+static bool
+get_sockaddr_by_host(const char *host, short destport,
+ struct sockaddr_in *addr,
+ GError **error_r)
+{
+ struct hostent *h;
+
+ h = gethostbyname(host);
+ if (h) {
+ addr->sin_family = h->h_addrtype;
+ memcpy((char *) &addr->sin_addr.s_addr, h->h_addr_list[0], h->h_length);
+ } else {
+ addr->sin_family = AF_INET;
+ if ((addr->sin_addr.s_addr=inet_addr(host))==0xFFFFFFFF) {
+ g_set_error(error_r, rtsp_client_quark(), 0,
+ "failed to resolve host '%s'", host);
+ return false;
+ }
+ }
+ addr->sin_port = htons(destport);
+ return true;
+}
+
+/*
+ * Calculate the current NTP time, store it in the buffer.
+ */
+static void
+fill_int(unsigned char *buffer, uint32_t value)
+{
+ uint32_t be = GINT32_TO_BE(value);
+ memcpy(buffer, &be, sizeof(be));
+}
+
+/*
+ * Store time in the NTP format in the buffer
+ */
+static void
+fill_time_buffer_with_time(unsigned char *buffer, struct timeval *tout)
+{
+ unsigned long secs_to_baseline = 964697997;
+ double fraction;
+ unsigned long long_fraction;
+ unsigned long secs;
+
+ fraction = ((double) tout->tv_usec) / 1000000.0;
+ long_fraction = (unsigned long) (fraction * 256.0 * 256.0 * 256.0 * 256.0);
+ secs = secs_to_baseline + tout->tv_sec;
+ fill_int(buffer, secs);
+ fill_int(buffer + 4, long_fraction);
+}
+
+static void
+get_time_for_rtp(struct play_state *state, struct timeval *tout)
+{
+ unsigned long rtp_diff = state->rtptime - state->start_rtptime;
+ unsigned long add_secs = rtp_diff / 44100;
+ unsigned long add_usecs = (((rtp_diff % 44100) * 10000) / 441) % 1000000;
+ tout->tv_sec = state->start_time.tv_sec + add_secs;
+ tout->tv_usec = state->start_time.tv_usec + add_usecs;
+ if (tout->tv_usec >= 1000000) {
+ tout->tv_sec++;
+ tout->tv_usec = tout->tv_usec % 1000000;
+ }
+}
+
+/*
+ * Send a control command
+ */
+static bool
+send_control_command(struct control_data *ctrl, struct raop_data *rd,
+ struct play_state *state,
+ GError **error_r)
+{
+ unsigned char buf[20];
+ int diff;
+ int num_bytes;
+ struct timeval ctrl_time;
+
+ diff = 88200;
+ if (rd->started) {
+ buf[0] = 0x80;
+ diff += NUMSAMPLES;
+ } else {
+ buf[0] = 0x90;
+ state->playing = true;
+ state->start_rtptime = state->rtptime;
+ }
+ buf[1] = 0xd4;
+ buf[2] = 0x00;
+ buf[3] = 0x07;
+ fill_int(buf + 4, state->rtptime - diff);
+ get_time_for_rtp(state, &ctrl_time);
+ fill_time_buffer_with_time(buf + 8, &ctrl_time);
+ fill_int(buf + 16, state->rtptime);
+
+ num_bytes = sendto(ctrl->fd, (const void *)buf, sizeof(buf), 0,
+ (struct sockaddr *)&rd->ctrl_addr,
+ sizeof(rd->ctrl_addr));
+ if (num_bytes < 0) {
+ g_set_error(error_r, raop_output_quark(), errno,
+ "Unable to send control command: %s",
+ g_strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+static int rsa_encrypt(const unsigned char *text, int len, unsigned char *res)
+{
+ RSA *rsa;
+ gsize usize;
+ unsigned char *modulus;
+ unsigned char *exponent;
+ int size;
+
+ char n[] =
+ "59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUtwC"
+ "5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDR"
+ "KSKv6kDqnw4UwPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuB"
+ "OitnZ/bDzPHrTOZz0Dew0uowxf/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJ"
+ "Q+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/UAaHqn9JdsBWLUEpVviYnh"
+ "imNVvYFZeCXg/IdTQ+x4IRdiXNv5hEew==";
+ char e[] = "AQAB";
+
+ rsa = RSA_new();
+
+ modulus = g_base64_decode(n, &usize);
+ rsa->n = BN_bin2bn(modulus, usize, NULL);
+ exponent = g_base64_decode(e, &usize);
+ rsa->e = BN_bin2bn(exponent, usize, NULL);
+ g_free(modulus);
+ g_free(exponent);
+ size = RSA_public_encrypt(len, text, res, rsa, RSA_PKCS1_OAEP_PADDING);
+
+ RSA_free(rsa);
+ return size;
+}
+
+static int
+raop_encrypt(struct encrypt_data *encryp, unsigned char *data, int size)
+{
+ // any bytes that fall beyond the last 16 byte page should be sent
+ // in the clear
+ int alt_size = size - (size % 16);
+
+ memcpy(encryp->nv, encryp->iv, 16);
+
+ AES_cbc_encrypt(data, data, alt_size, &encryp->ctx, encryp->nv, 1);
+
+ return size;
+}
+
+/* write bits filed data, *bpos=0 for msb, *bpos=7 for lsb
+ d=data, blen=length of bits field
+*/
+static inline void
+bits_write(unsigned char **p, unsigned char d, int blen, int *bpos)
+{
+ int lb, rb, bd;
+ lb =7 - *bpos;
+ rb = lb - blen + 1;
+ if (rb >= 0) {
+ bd = d << rb;
+ if (*bpos)
+ **p |= bd;
+ else
+ **p = bd;
+ *bpos += blen;
+ } else {
+ bd = d >> -rb;
+ **p |= bd;
+ *p += 1;
+ **p = d << (8 + rb);
+ *bpos = -rb;
+ }
+}
+
+static bool
+wrap_pcm(unsigned char *buffer, int bsize, int *size, unsigned char *inData, int inSize)
+{
+ unsigned char one[4];
+ int count = 0;
+ int bpos = 0;
+ unsigned char *bp = buffer;
+ int i, nodata = 0;
+ bits_write(&bp, 1, 3, &bpos); // channel=1, stereo
+ bits_write(&bp, 0, 4, &bpos); // unknown
+ bits_write(&bp, 0, 8, &bpos); // unknown
+ bits_write(&bp, 0, 4, &bpos); // unknown
+ if (bsize != 4096 && false)
+ bits_write(&bp, 1, 1, &bpos); // hassize
+ else
+ bits_write(&bp, 0, 1, &bpos); // hassize
+ bits_write(&bp, 0, 2, &bpos); // unused
+ bits_write(&bp, 1, 1, &bpos); // is-not-compressed
+ if (bsize != 4096 && false) {
+ // size of data, integer, big endian
+ bits_write(&bp, (bsize >> 24) & 0xff, 8, &bpos);
+ bits_write(&bp, (bsize >> 16) & 0xff, 8, &bpos);
+ bits_write(&bp, (bsize >> 8) & 0xff, 8, &bpos);
+ bits_write(&bp, bsize&0xff, 8, &bpos);
+ }
+ while (1) {
+ if (inSize <= count * 4) nodata = 1;
+ if (nodata) break;
+ one[0] = inData[count * 4];
+ one[1] = inData[count * 4 + 1];
+ one[2] = inData[count * 4 + 2];
+ one[3] = inData[count * 4 + 3];
+
+#if BYTE_ORDER == BIG_ENDIAN
+ bits_write(&bp, one[0], 8, &bpos);
+ bits_write(&bp, one[1], 8, &bpos);
+ bits_write(&bp, one[2], 8, &bpos);
+ bits_write(&bp, one[3], 8, &bpos);
+#else
+ bits_write(&bp, one[1], 8, &bpos);
+ bits_write(&bp, one[0], 8, &bpos);
+ bits_write(&bp, one[3], 8, &bpos);
+ bits_write(&bp, one[2], 8, &bpos);
+#endif
+
+ if (++count == bsize) break;
+ }
+ if (!count) return false; // when no data at all, it should stop playing
+ /* when readable size is less than bsize, fill 0 at the bottom */
+ for(i = 0; i < (bsize - count) * 4; i++) {
+ bits_write(&bp, 0, 8, &bpos);
+ }
+ *size = (int)(bp - buffer);
+ if (bpos) *size += 1;
+ return true;
+}
+
+static bool
+raopcl_connect(struct raop_data *rd, GError **error_r)
+{
+ unsigned char buf[4 + 8 + 16];
+ char sid[16];
+ char sci[24];
+ char act_r[17];
+ char *sac=NULL, *key = NULL, *iv = NULL;
+ char sdp[1024];
+ int rval = false;
+ unsigned char rsakey[512];
+ struct timeval current_time;
+ unsigned int sessionNum;
+ int i;
+
+
+ gettimeofday(&current_time,NULL);
+ sessionNum = current_time.tv_sec + 2082844804;
+
+ RAND_bytes(buf, sizeof(buf));
+ sprintf(act_r, "%u", (unsigned int) g_random_int());
+ sprintf(sid, "%u", sessionNum);
+ sprintf(sci, "%08x%08x", *((int *)(buf + 4)), *((int *)(buf + 8)));
+ sac = g_base64_encode(buf + 12, 16);
+ rd->rtspcl = rtspcl_open();
+ rtspcl_set_useragent(rd->rtspcl, "iTunes/8.1.1 (Macintosh; U; PPC Mac OS X 10.4)");
+ rtspcl_add_exthds(rd->rtspcl, "Client-Instance", sci);
+ rtspcl_add_exthds(rd->rtspcl, "DACP-ID", sci);
+ rtspcl_add_exthds(rd->rtspcl, "Active-Remote", act_r);
+ if (!rtspcl_connect(rd->rtspcl, rd->addr, rd->rtsp_port, sid, error_r))
+ goto erexit;
+
+ i = rsa_encrypt(raop_session->encrypt.key, 16, rsakey);
+ key = g_base64_encode(rsakey, i);
+ remove_char_from_string(key, '=');
+ iv = g_base64_encode(raop_session->encrypt.iv, 16);
+ remove_char_from_string(iv, '=');
+ sprintf(sdp,
+ "v=0\r\n"
+ "o=iTunes %s 0 IN IP4 %s\r\n"
+ "s=iTunes\r\n"
+ "c=IN IP4 %s\r\n"
+ "t=0 0\r\n"
+ "m=audio 0 RTP/AVP 96\r\n"
+ "a=rtpmap:96 AppleLossless\r\n"
+ "a=fmtp:96 %d 0 16 40 10 14 2 255 0 0 44100\r\n"
+ "a=rsaaeskey:%s\r\n"
+ "a=aesiv:%s\r\n",
+ sid, rtspcl_local_ip(rd->rtspcl), rd->addr, NUMSAMPLES, key, iv);
+ remove_char_from_string(sac, '=');
+ // rtspcl_add_exthds(rd->rtspcl, "Apple-Challenge", sac);
+ if (!rtspcl_announce_sdp(rd->rtspcl, sdp, error_r))
+ goto erexit;
+ // if (!rtspcl_mark_del_exthds(rd->rtspcl, "Apple-Challenge")) goto erexit;
+ if (!rtspcl_setup(rd->rtspcl, NULL,
+ raop_session->ctrl.port, raop_session->ntp.port,
+ error_r))
+ goto erexit;
+
+ if (!get_sockaddr_by_host(rd->addr, rd->rtspcl->control_port,
+ &rd->ctrl_addr, error_r))
+ goto erexit;
+
+ if (!get_sockaddr_by_host(rd->addr, rd->rtspcl->server_port,
+ &rd->data_addr, error_r))
+ goto erexit;
+
+ if (!rtspcl_record(rd->rtspcl,
+ raop_session->play_state.seq_num,
+ raop_session->play_state.rtptime,
+ error_r))
+ goto erexit;
+
+ rval = true;
+
+ erexit:
+ g_free(sac);
+ g_free(key);
+ g_free(iv);
+ return rval;
+}
+
+static int
+difference (struct timeval *t1, struct timeval *t2)
+{
+ int ret = 150000000;
+ if (t1->tv_sec - t2->tv_sec < 150) {
+ ret = (t1->tv_sec - t2->tv_sec) * 1000000;
+ ret += t1->tv_usec - t2->tv_usec;
+ }
+ return ret;
+}
+
+/*
+ * With airtunes version 2, we don't get responses back when we send audio
+ * data. The only requests we get from the airtunes device are timing
+ * requests.
+ */
+static bool
+send_audio_data(int fd, GError **error_r)
+{
+ int i = 0;
+ struct timeval current_time, rtp_time;
+ struct raop_data *rd = raop_session->raop_list;
+
+ get_time_for_rtp(&raop_session->play_state, &rtp_time);
+ gettimeofday(&current_time, NULL);
+ int diff = difference(&rtp_time, &current_time);
+ if (diff > 0)
+ g_usleep(diff);
+
+ gettimeofday(&raop_session->play_state.last_send, NULL);
+ while (rd) {
+ if (rd->started) {
+ raop_session->data[1] = 0x60;
+ } else {
+ rd->started = true;
+ raop_session->data[1] = 0xe0;
+ }
+ i = sendto(fd, (const void *)(raop_session->data + raop_session->wblk_wsize),
+ raop_session->wblk_remsize, 0, (struct sockaddr *) &rd->data_addr,
+ sizeof(rd->data_addr));
+ if (i < 0) {
+ g_set_error(error_r, raop_output_quark(), errno,
+ "write error: %s",
+ g_strerror(errno));
+ return false;
+ }
+ if (i == 0) {
+ g_set_error_literal(error_r, raop_output_quark(), 0,
+ "disconnected on the other end");
+ return false;
+ }
+ rd = rd->next;
+ }
+ raop_session->wblk_wsize += i;
+ raop_session->wblk_remsize -= i;
+
+ return true;
+}
+
+static void *
+raop_output_init(G_GNUC_UNUSED const struct audio_format *audio_format,
+ G_GNUC_UNUSED const struct config_param *param,
+ GError **error_r)
+{
+ const char *host = config_get_block_string(param, "host", NULL);
+ if (host == NULL) {
+ g_set_error_literal(error_r, raop_output_quark(), 0,
+ "missing option 'host'");
+ return NULL;
+ }
+
+ struct raop_data *rd;
+
+ rd = new_raop_data(error_r);
+ if (rd == NULL)
+ return NULL;
+
+ rd->addr = host;
+ rd->rtsp_port = config_get_block_unsigned(param, "port", 5000);
+ rd->volume = config_get_block_unsigned(param, "volume", 75);
+ return rd;
+}
+
+static bool
+raop_set_volume_local(struct raop_data *rd, int volume, GError **error_r)
+{
+ char vol_str[128];
+ sprintf(vol_str, "volume: %d.000000\r\n", volume);
+ return rtspcl_set_parameter(rd->rtspcl, vol_str, error_r);
+}
+
+
+static void
+raop_output_finish(void *data)
+{
+ struct raop_data *rd = data;
+
+ if (rd->rtspcl)
+ rtspcl_close(rd->rtspcl);
+
+ g_mutex_free(rd->control_mutex);
+ g_free(rd);
+}
+
+#define RAOP_VOLUME_MIN -30
+#define RAOP_VOLUME_MAX 0
+
+int
+raop_get_volume(struct raop_data *rd)
+{
+ return rd->volume;
+}
+
+bool
+raop_set_volume(struct raop_data *rd, unsigned volume, GError **error_r)
+{
+ int raop_volume;
+ bool rval;
+
+ //set parameter volume
+ if (volume == 0) {
+ raop_volume = -144;
+ } else {
+ raop_volume = RAOP_VOLUME_MIN +
+ (RAOP_VOLUME_MAX - RAOP_VOLUME_MIN) * volume / 100;
+ }
+ g_mutex_lock(rd->control_mutex);
+ rval = raop_set_volume_local(rd, raop_volume, error_r);
+ if (rval) rd->volume = volume;
+ g_mutex_unlock(rd->control_mutex);
+
+ return rval;
+}
+
+static void
+raop_output_cancel(void *data)
+{
+ //flush
+ struct key_data kd;
+ struct raop_data *rd = (struct raop_data *) data;
+ int flush_diff = 1;
+
+ rd->started = 0;
+ if (rd->is_master) {
+ raop_session->play_state.playing = false;
+ }
+ if (rd->paused) {
+ return;
+ }
+
+ g_mutex_lock(rd->control_mutex);
+ static char rtp_key[] = "RTP-Info";
+ kd.key = rtp_key;
+ char buf[128];
+ sprintf(buf, "seq=%d; rtptime=%d", raop_session->play_state.seq_num + flush_diff, raop_session->play_state.rtptime + NUMSAMPLES * flush_diff);
+ kd.data = buf;
+ kd.next = NULL;
+ exec_request(rd->rtspcl, "FLUSH", NULL, NULL, 1,
+ &kd, NULL, NULL);
+ g_mutex_unlock(rd->control_mutex);
+}
+
+static bool
+raop_output_pause(void *data)
+{
+ struct raop_data *rd = (struct raop_data *) data;
+
+ rd->paused = true;
+ return true;
+}
+
+/**
+ * Remove the output from the session's list. Caller must not lock
+ * the list_mutex.
+ */
+static void
+raop_output_remove(struct raop_data *rd)
+{
+ struct raop_data *iter = raop_session->raop_list;
+ struct raop_data *prev = NULL;
+
+ g_mutex_lock(raop_session->list_mutex);
+ while (iter) {
+ if (iter == rd) {
+ if (prev != NULL) {
+ prev->next = rd->next;
+ } else {
+ raop_session->raop_list = rd->next;
+ }
+ if (rd->is_master && raop_session->raop_list != NULL) {
+ raop_session->raop_list->is_master = true;
+ }
+ rd->next = NULL;
+ rd->is_master = false;
+ break;
+ }
+ prev = iter;
+ iter = iter->next;
+ }
+ g_mutex_unlock(raop_session->list_mutex);
+
+ if (raop_session->raop_list == NULL) {
+ raop_session_free(raop_session);
+ raop_session = NULL;
+ }
+}
+
+static void
+raop_output_close(void *data)
+{
+ //teardown
+ struct raop_data *rd = data;
+
+ raop_output_remove(rd);
+
+ g_mutex_lock(rd->control_mutex);
+ exec_request(rd->rtspcl, "TEARDOWN", NULL, NULL, 0,
+ NULL, NULL, NULL);
+ g_mutex_unlock(rd->control_mutex);
+
+ rd->started = 0;
+}
+
+
+static bool
+raop_output_open(void *data, struct audio_format *audio_format, GError **error_r)
+{
+ //setup, etc.
+ struct raop_data *rd = data;
+
+ g_mutex_lock(raop_session->list_mutex);
+ if (raop_session->raop_list == NULL) {
+ // first raop, need to initialize session data
+ unsigned short myport = 0;
+ raop_session->raop_list = rd;
+ rd->is_master = true;
+
+ raop_session->data_fd = open_udp_socket(NULL, &myport,
+ error_r);
+ if (raop_session->data_fd < 0)
+ return false;
+
+ if (!ntp_server_open(&raop_session->ntp, error_r))
+ return false;
+
+ raop_session->ctrl.fd =
+ open_udp_socket(NULL, &raop_session->ctrl.port,
+ error_r);
+ if (raop_session->ctrl.fd < 0) {
+ ntp_server_close(&raop_session->ntp);
+ raop_session->ctrl.fd = -1;
+ g_mutex_unlock(raop_session->list_mutex);
+ return false;
+ }
+ }
+ g_mutex_unlock(raop_session->list_mutex);
+
+ audio_format->format = SAMPLE_FORMAT_S16;
+ if (!raopcl_connect(rd, error_r)) {
+ raop_output_remove(rd);
+ return false;
+ }
+
+ if (!raop_set_volume(rd, rd->volume, error_r)) {
+ raop_output_remove(rd);
+ return false;
+ }
+
+ g_mutex_lock(raop_session->list_mutex);
+ if (!rd->is_master) {
+ rd->next = raop_session->raop_list;
+ raop_session->raop_list = rd;
+ }
+ g_mutex_unlock(raop_session->list_mutex);
+ return true;
+}
+
+static size_t
+raop_output_play(void *data, const void *chunk, size_t size,
+ GError **error_r)
+{
+ //raopcl_send_sample
+ struct raop_data *rd = data;
+ size_t rval = 0, orig_size = size;
+
+ rd->paused = false;
+ if (!rd->is_master) {
+ // only process data for the master raop
+ return size;
+ }
+
+ g_mutex_lock(raop_session->data_mutex);
+
+ if (raop_session->play_state.rtptime <= NUMSAMPLES) {
+ // looped over, need new reference point to calculate correct times
+ raop_session->play_state.playing = false;
+ }
+
+ while (raop_session->bufferSize + size >= RAOP_BUFFER_SIZE) {
+ // ntp header
+ unsigned char header[] = {
+ 0x80, 0x60, 0x00, 0x00,
+ // rtptime
+ 0x00, 0x00, 0x00, 0x00,
+ // device
+ 0x7e, 0xad, 0xd2, 0xd3,
+ };
+
+
+ int count = 0;
+ int copyBytes = RAOP_BUFFER_SIZE - raop_session->bufferSize;
+
+ if (!raop_session->play_state.playing ||
+ raop_session->play_state.seq_num % (44100 / NUMSAMPLES + 1) == 0) {
+ struct raop_data *iter;
+ g_mutex_lock(raop_session->list_mutex);
+ if (!raop_session->play_state.playing) {
+ gettimeofday(&raop_session->play_state.start_time,NULL);
+ }
+ iter = raop_session->raop_list;
+ while (iter) {
+ if (!send_control_command(&raop_session->ctrl, iter,
+ &raop_session->play_state,
+ error_r))
+ goto erexit;
+
+ iter = iter->next;
+ }
+ g_mutex_unlock(raop_session->list_mutex);
+ }
+
+ fill_int(header + 8, raop_session->play_state.sync_src);
+
+ memcpy(raop_session->buffer + raop_session->bufferSize, chunk, copyBytes);
+ raop_session->bufferSize += copyBytes;
+ chunk = ((const char *)chunk) + copyBytes;
+ size -= copyBytes;
+
+ if (!wrap_pcm(raop_session->data + RAOP_HEADER_SIZE, NUMSAMPLES, &count, raop_session->buffer, RAOP_BUFFER_SIZE)) {
+ g_warning("unable to encode %d bytes properly\n", RAOP_BUFFER_SIZE);
+ }
+
+ memcpy(raop_session->data, header, RAOP_HEADER_SIZE);
+ raop_session->data[2] = raop_session->play_state.seq_num >> 8;
+ raop_session->data[3] = raop_session->play_state.seq_num & 0xff;
+ raop_session->play_state.seq_num ++;
+
+ fill_int(raop_session->data + 4, raop_session->play_state.rtptime);
+ raop_session->play_state.rtptime += NUMSAMPLES;
+
+ raop_encrypt(&raop_session->encrypt, raop_session->data + RAOP_HEADER_SIZE, count);
+ raop_session->wblk_remsize = count + RAOP_HEADER_SIZE;
+ raop_session->wblk_wsize = 0;
+
+ if (!send_audio_data(raop_session->data_fd, error_r))
+ goto erexit;
+
+ raop_session->bufferSize = 0;
+ }
+ if (size > 0) {
+ memcpy(raop_session->buffer + raop_session->bufferSize, chunk, size);
+ raop_session->bufferSize += size;
+ }
+ rval = orig_size;
+ erexit:
+ g_mutex_unlock(raop_session->data_mutex);
+ return rval;
+}
+
+const struct audio_output_plugin raopPlugin = {
+ .name = "raop",
+ .init = raop_output_init,
+ .finish = raop_output_finish,
+ .open = raop_output_open,
+ .play = raop_output_play,
+ .cancel = raop_output_cancel,
+ .pause = raop_output_pause,
+ .close = raop_output_close,
+ .mixer_plugin = &raop_mixer_plugin,
+};
diff --git a/src/output/raop_output_plugin.h b/src/output/raop_output_plugin.h
new file mode 100644
index 000000000..210237179
--- /dev/null
+++ b/src/output/raop_output_plugin.h
@@ -0,0 +1,129 @@
+/*
+ * 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_OUTPUT_RAOP_PLUGIN_H
+#define MPD_OUTPUT_RAOP_PLUGIN_H
+
+#include "ntp_server.h"
+#include "rtsp_client.h"
+
+#include <glib.h>
+#include <stdbool.h>
+#include <sys/time.h>
+#include <openssl/aes.h>
+
+struct play_state {
+ bool playing;
+ unsigned short seq_num;
+ unsigned int rtptime;
+ unsigned int sync_src;
+ unsigned int start_rtptime;
+ struct timeval start_time;
+ struct timeval last_send;
+};
+
+/*********************************************************************/
+
+enum pause_state {
+ NO_PAUSE = 0,
+ OP_PAUSE,
+ NODATA_PAUSE,
+};
+
+#define MINIMUM_SAMPLE_SIZE 32
+
+#define RAOP_FD_READ (1<<0)
+#define RAOP_FD_WRITE (1<<1)
+
+/*********************************************************************/
+
+struct encrypt_data {
+ AES_KEY ctx;
+ unsigned char iv[16]; // initialization vector for aes-cbc
+ unsigned char nv[16]; // next vector for aes-cbc
+ unsigned char key[16]; // key for aes-cbc
+};
+
+/*********************************************************************/
+
+struct raop_data {
+ struct rtspcl_data *rtspcl;
+ const char *addr; // target host address
+ short rtsp_port;
+ struct sockaddr_in ctrl_addr;
+ struct sockaddr_in data_addr;
+
+ bool is_master;
+ struct raop_data *next;
+
+ unsigned volume;
+
+ GMutex *control_mutex;
+
+ bool started;
+ bool paused;
+};
+
+/*********************************************************************/
+
+struct control_data {
+ unsigned short port;
+ int fd;
+};
+
+/*********************************************************************/
+
+#define NUMSAMPLES 352
+#define RAOP_BUFFER_SIZE NUMSAMPLES * 4
+#define RAOP_HEADER_SIZE 12
+#define ALAC_MAX_HEADER_SIZE 8
+#define RAOP_MAX_PACKET_SIZE RAOP_BUFFER_SIZE + RAOP_HEADER_SIZE + ALAC_MAX_HEADER_SIZE
+
+// session
+struct raop_session_data {
+ struct raop_data *raop_list;
+ struct ntp_server ntp;
+ struct control_data ctrl;
+ struct encrypt_data encrypt;
+ struct play_state play_state;
+
+ int data_fd;
+
+ unsigned char buffer[RAOP_BUFFER_SIZE];
+ size_t bufferSize;
+
+ unsigned char data[RAOP_MAX_PACKET_SIZE];
+ int wblk_wsize;
+ int wblk_remsize;
+
+ GMutex *data_mutex;
+ GMutex *list_mutex;
+};
+
+//static struct raop_session_data *raop_session;
+
+/*********************************************************************/
+
+bool
+raop_set_volume(struct raop_data *rd, unsigned volume, GError **error_r);
+
+int
+raop_get_volume(struct raop_data *rd);
+
+#endif
diff --git a/src/output/recorder_output_plugin.c b/src/output/recorder_output_plugin.c
index 10d64106c..070ef4b08 100644
--- a/src/output/recorder_output_plugin.c
+++ b/src/output/recorder_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/roar_output_plugin.h b/src/output/roar_output_plugin.h
new file mode 100644
index 000000000..08e272007
--- /dev/null
+++ b/src/output/roar_output_plugin.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
+ * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
+ *
+ * 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 __ROAR_OUTPUT_H
+#define __ROAR_OUTPUT_H
+
+#include <roaraudio.h>
+#include <glib.h>
+
+typedef struct roar
+{
+ roar_vs_t * vss;
+ int err;
+ char *host;
+ char *name;
+ int role;
+ struct roar_connection con;
+ struct roar_audio_info info;
+ GMutex *lock;
+ volatile bool alive;
+} roar_t;
+
+#endif
diff --git a/src/output/roar_plugin.c b/src/output/roar_plugin.c
new file mode 100644
index 000000000..f9d44a3d8
--- /dev/null
+++ b/src/output/roar_plugin.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2010-2011 Philipp 'ph3-der-loewe' Schafft
+ * Copyright (C) 2010-2011 Hans-Kristian 'maister' Arntzen
+ *
+ * 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 "output_api.h"
+#include "mixer_list.h"
+#include "roar_output_plugin.h"
+
+#include <glib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "roaraudio"
+
+static inline GQuark
+roar_output_quark(void)
+{
+ return g_quark_from_static_string("roar_output");
+}
+
+static void
+roar_configure(struct roar * self, const struct config_param *param)
+{
+ self->host = config_dup_block_string(param, "server", NULL);
+ self->name = config_dup_block_string(param, "name", "MPD");
+ char *role = config_dup_block_string(param, "role", "music");
+ if (role != NULL)
+ {
+ self->role = roar_str2role(role);
+ g_free(role);
+ }
+ else
+ self->role = ROAR_ROLE_MUSIC;
+}
+
+static void *
+roar_init(G_GNUC_UNUSED const struct audio_format *audio_format,
+ const struct config_param *param,
+ G_GNUC_UNUSED GError **error)
+{
+ GMutex *lock = g_mutex_new();
+
+ roar_t * self = roar_mm_calloc(1, sizeof(*self));
+ if (self == NULL)
+ {
+ g_set_error(error, roar_output_quark(), 0, "Failed to allocate memory");
+ return NULL;
+ }
+
+ self->lock = lock;
+ self->err = ROAR_ERROR_NONE;
+ roar_configure(self, param);
+ return self;
+}
+
+static void
+roar_close(void *data)
+{
+ roar_t * self = data;
+ g_mutex_lock(self->lock);
+ self->alive = false;
+
+ if (self->vss != NULL)
+ roar_vs_close(self->vss, ROAR_VS_TRUE, &(self->err));
+ self->vss = NULL;
+ roar_disconnect(&(self->con));
+ g_mutex_unlock(self->lock);
+}
+
+static void
+roar_finish(void *data)
+{
+ roar_t * self = data;
+
+ g_free(self->host);
+ g_free(self->name);
+ g_mutex_free(self->lock);
+
+ roar_mm_free(data);
+}
+
+static bool
+roar_open(void *data, struct audio_format *audio_format, GError **error)
+{
+ roar_t * self = data;
+ g_mutex_lock(self->lock);
+
+ if (roar_simple_connect(&(self->con), self->host, self->name) < 0)
+ {
+ g_set_error(error, roar_output_quark(), 0,
+ "Failed to connect to Roar server");
+ g_mutex_unlock(self->lock);
+ return false;
+ }
+
+ self->vss = roar_vs_new_from_con(&(self->con), &(self->err));
+
+ if (self->vss == NULL || self->err != ROAR_ERROR_NONE)
+ {
+ g_set_error(error, roar_output_quark(), 0,
+ "Failed to connect to server");
+ g_mutex_unlock(self->lock);
+ return false;
+ }
+
+ self->info.rate = audio_format->sample_rate;
+ self->info.channels = audio_format->channels;
+ self->info.codec = ROAR_CODEC_PCM_S;
+
+ switch (audio_format->format)
+ {
+ case SAMPLE_FORMAT_S8:
+ self->info.bits = 8;
+ break;
+ case SAMPLE_FORMAT_S16:
+ self->info.bits = 16;
+ break;
+ case SAMPLE_FORMAT_S24:
+ self->info.bits = 24;
+ break;
+ case SAMPLE_FORMAT_S24_P32:
+ self->info.bits = 32;
+ audio_format->format = SAMPLE_FORMAT_S32;
+ break;
+ case SAMPLE_FORMAT_S32:
+ self->info.bits = 32;
+ break;
+ default:
+ self->info.bits = 16;
+ audio_format->format = SAMPLE_FORMAT_S16;
+ }
+ audio_format->reverse_endian = 0;
+
+ if (roar_vs_stream(self->vss, &(self->info), ROAR_DIR_PLAY,
+ &(self->err)) < 0)
+ {
+ g_set_error(error, roar_output_quark(), 0, "Failed to start stream");
+ g_mutex_unlock(self->lock);
+ return false;
+ }
+ roar_vs_role(self->vss, self->role, &(self->err));
+ self->alive = true;
+
+ g_mutex_unlock(self->lock);
+ return true;
+}
+
+static void
+roar_cancel(void *data)
+{
+ roar_t * self = data;
+
+ g_mutex_lock(self->lock);
+ if (self->vss != NULL)
+ {
+ roar_vs_t *vss = self->vss;
+ self->vss = NULL;
+ roar_vs_close(vss, ROAR_VS_TRUE, &(self->err));
+ self->alive = false;
+
+ vss = roar_vs_new_from_con(&(self->con), &(self->err));
+ if (vss)
+ {
+ roar_vs_stream(vss, &(self->info), ROAR_DIR_PLAY, &(self->err));
+ roar_vs_role(vss, self->role, &(self->err));
+ self->vss = vss;
+ self->alive = true;
+ }
+ }
+ g_mutex_unlock(self->lock);
+}
+
+static size_t
+roar_play(void *data, const void *chunk, size_t size, GError **error)
+{
+ struct roar * self = data;
+ ssize_t rc;
+
+ if (self->vss == NULL)
+ {
+ g_set_error(error, roar_output_quark(), 0, "Connection is invalid");
+ return 0;
+ }
+
+ rc = roar_vs_write(self->vss, chunk, size, &(self->err));
+ if ( rc <= 0 )
+ {
+ g_set_error(error, roar_output_quark(), 0, "Failed to play data");
+ return 0;
+ }
+
+ return rc;
+}
+
+static const char*
+roar_tag_convert(enum tag_type type, bool *is_uuid)
+{
+ *is_uuid = false;
+ switch (type)
+ {
+ case TAG_ARTIST:
+ case TAG_ALBUM_ARTIST:
+ return "AUTHOR";
+ case TAG_ALBUM:
+ return "ALBUM";
+ case TAG_TITLE:
+ return "TITLE";
+ case TAG_TRACK:
+ return "TRACK";
+ case TAG_NAME:
+ return "NAME";
+ case TAG_GENRE:
+ return "GENRE";
+ case TAG_DATE:
+ return "DATE";
+ case TAG_PERFORMER:
+ return "PERFORMER";
+ case TAG_COMMENT:
+ return "COMMENT";
+ case TAG_DISC:
+ return "DISCID";
+ case TAG_COMPOSER:
+#ifdef ROAR_META_TYPE_COMPOSER
+ return "COMPOSER";
+#else
+ return "AUTHOR";
+#endif
+ case TAG_MUSICBRAINZ_ARTISTID:
+ case TAG_MUSICBRAINZ_ALBUMID:
+ case TAG_MUSICBRAINZ_ALBUMARTISTID:
+ case TAG_MUSICBRAINZ_TRACKID:
+ *is_uuid = true;
+ return "HASH";
+
+ default:
+ return NULL;
+ }
+}
+
+static void
+roar_send_tag(void *data, const struct tag *meta)
+{
+ struct roar * self = data;
+
+ if (self->vss == NULL)
+ return;
+
+ g_mutex_lock(self->lock);
+ size_t cnt = 1;
+ struct roar_keyval vals[32];
+ memset(vals, 0, sizeof(vals));
+ char uuid_buf[32][64];
+
+ char timebuf[16];
+ snprintf(timebuf, sizeof(timebuf), "%02d:%02d:%02d",
+ meta->time / 3600, (meta->time % 3600) / 60, meta->time % 60);
+
+ vals[0].key = g_strdup("LENGTH");
+ vals[0].value = timebuf;
+
+ for (unsigned i = 0; i < meta->num_items && cnt < 32; i++)
+ {
+ bool is_uuid = false;
+ const char *key = roar_tag_convert(meta->items[i]->type, &is_uuid);
+ if (key != NULL)
+ {
+ if (is_uuid)
+ {
+ snprintf(uuid_buf[cnt], sizeof(uuid_buf[0]), "{UUID}%s",
+ meta->items[i]->value);
+ vals[cnt].key = g_strdup(key);
+ vals[cnt].value = uuid_buf[cnt];
+ }
+ else
+ {
+ vals[cnt].key = g_strdup(key);
+ vals[cnt].value = meta->items[i]->value;
+ }
+ cnt++;
+ }
+ }
+
+ roar_vs_meta(self->vss, vals, cnt, &(self->err));
+
+ for (unsigned i = 0; i < 32; i++)
+ g_free(vals[i].key);
+
+ g_mutex_unlock(self->lock);
+}
+
+const struct audio_output_plugin roar_output_plugin = {
+ .name = "roar",
+ .init = roar_init,
+ .finish = roar_finish,
+ .open = roar_open,
+ .play = roar_play,
+ .cancel = roar_cancel,
+ .close = roar_close,
+ .send_tag = roar_send_tag,
+
+ .mixer_plugin = &roar_mixer_plugin
+};
diff --git a/src/output/shout_plugin.c b/src/output/shout_plugin.c
index 35efd9fc7..80adf1638 100644
--- a/src/output/shout_plugin.c
+++ b/src/output/shout_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -125,7 +125,7 @@ my_shout_init_driver(const struct audio_format *audio_format,
const char *user;
char *name;
const char *value;
- struct block_param *block_param;
+ const struct block_param *block_param;
int public;
if (audio_format == NULL ||
@@ -277,6 +277,13 @@ my_shout_init_driver(const struct audio_format *audio_format,
goto failure;
}
+ value = config_get_block_string(param, "url", NULL);
+ if (value != NULL && shout_set_url(sd->shout_conn, value)) {
+ g_set_error(error, shout_output_quark(), 0,
+ "%s", shout_get_error(sd->shout_conn));
+ goto failure;
+ }
+
{
char temp[11];
memset(temp, 0, sizeof(temp));
diff --git a/src/output/solaris_output_plugin.c b/src/output/solaris_output_plugin.c
index 22c583805..c0d38defb 100644
--- a/src/output/solaris_output_plugin.c
+++ b/src/output/solaris_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/winmm_output_plugin.c b/src/output/winmm_output_plugin.c
index b9687874d..e93a9f6c5 100644
--- a/src/output/winmm_output_plugin.c
+++ b/src/output/winmm_output_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output/winmm_output_plugin.h b/src/output/winmm_output_plugin.h
index 39507275a..10f0bb6b1 100644
--- a/src/output/winmm_output_plugin.h
+++ b/src/output/winmm_output_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_all.c b/src/output_all.c
index 4e0b2eb22..7f4694a8b 100644
--- a/src/output_all.c
+++ b/src/output_all.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -27,6 +27,7 @@
#include "buffer.h"
#include "player_control.h"
#include "mpd_error.h"
+#include "notify.h"
#ifndef NDEBUG
#include "chunk.h"
@@ -100,7 +101,7 @@ audio_output_config_count(void)
}
void
-audio_output_all_init(void)
+audio_output_all_init(struct player_control *pc)
{
const struct config_param *param = NULL;
unsigned int i;
@@ -121,7 +122,7 @@ audio_output_all_init(void)
/* only allow param to be NULL if there just one audioOutput */
assert(param || (num_audio_outputs == 1));
- if (!audio_output_init(output, param, &error)) {
+ if (!audio_output_init(output, param, pc, &error)) {
if (param != NULL)
MPD_ERROR("line %i: %s",
param->line, error->message);
@@ -460,17 +461,17 @@ audio_output_all_check(void)
}
bool
-audio_output_all_wait(unsigned threshold)
+audio_output_all_wait(struct player_control *pc, unsigned threshold)
{
- player_lock();
+ player_lock(pc);
if (audio_output_all_check() < threshold) {
- player_unlock();
+ player_unlock(pc);
return true;
}
- player_wait();
- player_unlock();
+ player_wait(pc);
+ player_unlock(pc);
return audio_output_all_check() < threshold;
}
diff --git a/src/output_all.h b/src/output_all.h
index a579bf5f1..4eeb94f13 100644
--- a/src/output_all.h
+++ b/src/output_all.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -32,13 +32,14 @@
struct audio_format;
struct music_buffer;
struct music_chunk;
+struct player_control;
/**
* Global initialization: load audio outputs from the configuration
* file and initialize them.
*/
void
-audio_output_all_init(void);
+audio_output_all_init(struct player_control *pc);
/**
* Global finalization: free memory occupied by audio outputs. All
@@ -127,7 +128,7 @@ audio_output_all_check(void);
* @return true if there are less than #threshold chunks in the pipe
*/
bool
-audio_output_all_wait(unsigned threshold);
+audio_output_all_wait(struct player_control *pc, unsigned threshold);
/**
* Puts all audio outputs into pause mode. Most implementations will
diff --git a/src/output_api.h b/src/output_api.h
index 8e002dd48..302a0cdc9 100644
--- a/src/output_api.h
+++ b/src/output_api.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_command.c b/src/output_command.c
index 825884e8e..3988f350a 100644
--- a/src/output_command.c
+++ b/src/output_command.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -50,7 +50,7 @@ audio_output_enable_index(unsigned idx)
ao->enabled = true;
idle_add(IDLE_OUTPUT);
- pc_update_audio();
+ pc_update_audio(ao->player_control);
++audio_output_state_version;
@@ -79,7 +79,7 @@ audio_output_disable_index(unsigned idx)
idle_add(IDLE_MIXER);
}
- pc_update_audio();
+ pc_update_audio(ao->player_control);
++audio_output_state_version;
diff --git a/src/output_command.h b/src/output_command.h
index fab015c3f..eda30acc8 100644
--- a/src/output_command.h
+++ b/src/output_command.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_control.c b/src/output_control.c
index f8c5cd873..7ddcb8b19 100644
--- a/src/output_control.c
+++ b/src/output_control.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -38,6 +38,11 @@ enum {
struct notify audio_output_client_notify;
+/**
+ * Waits for command completion.
+ *
+ * @param ao the #audio_output instance; must be locked
+ */
static void ao_command_wait(struct audio_output *ao)
{
while (ao->command != AO_COMMAND_NONE) {
@@ -47,20 +52,43 @@ static void ao_command_wait(struct audio_output *ao)
}
}
-static void ao_command(struct audio_output *ao, enum audio_output_command cmd)
+/**
+ * Sends a command to the #audio_output object, but does not wait for
+ * completion.
+ *
+ * @param ao the #audio_output instance; must be locked
+ */
+static void ao_command_async(struct audio_output *ao,
+ enum audio_output_command cmd)
{
assert(ao->command == AO_COMMAND_NONE);
ao->command = cmd;
g_cond_signal(ao->cond);
+}
+
+/**
+ * Sends a command to the #audio_output object and waits for
+ * completion.
+ *
+ * @param ao the #audio_output instance; must be locked
+ */
+static void
+ao_command(struct audio_output *ao, enum audio_output_command cmd)
+{
+ ao_command_async(ao, cmd);
ao_command_wait(ao);
}
-static void ao_command_async(struct audio_output *ao,
- enum audio_output_command cmd)
+/**
+ * Lock the #audio_output object and execute the command
+ * synchronously.
+ */
+static void
+ao_lock_command(struct audio_output *ao, enum audio_output_command cmd)
{
- assert(ao->command == AO_COMMAND_NONE);
- ao->command = cmd;
- g_cond_signal(ao->cond);
+ g_mutex_lock(ao->mutex);
+ ao_command(ao, cmd);
+ g_mutex_unlock(ao->mutex);
}
void
@@ -78,9 +106,7 @@ audio_output_enable(struct audio_output *ao)
audio_output_thread_start(ao);
}
- g_mutex_lock(ao->mutex);
- ao_command(ao, AO_COMMAND_ENABLE);
- g_mutex_unlock(ao->mutex);
+ ao_lock_command(ao, AO_COMMAND_ENABLE);
}
void
@@ -97,9 +123,7 @@ audio_output_disable(struct audio_output *ao)
return;
}
- g_mutex_lock(ao->mutex);
- ao_command(ao, AO_COMMAND_DISABLE);
- g_mutex_unlock(ao->mutex);
+ ao_lock_command(ao, AO_COMMAND_DISABLE);
}
static void
@@ -305,28 +329,11 @@ void audio_output_finish(struct audio_output *ao)
assert(ao->fail_timer == NULL);
if (ao->thread != NULL) {
- g_mutex_lock(ao->mutex);
assert(ao->allow_play);
- ao_command(ao, AO_COMMAND_KILL);
- g_mutex_unlock(ao->mutex);
+ ao_lock_command(ao, AO_COMMAND_KILL);
g_thread_join(ao->thread);
+ ao->thread = NULL;
}
- if (ao->mixer != NULL)
- mixer_free(ao->mixer);
-
- ao_plugin_finish(ao->plugin, ao->data);
-
- g_cond_free(ao->cond);
- g_mutex_free(ao->mutex);
-
- if (ao->replay_gain_filter != NULL)
- filter_free(ao->replay_gain_filter);
-
- if (ao->other_replay_gain_filter != NULL)
- filter_free(ao->other_replay_gain_filter);
-
- filter_free(ao->filter);
-
- pcm_buffer_deinit(&ao->cross_fade_buffer);
+ audio_output_destruct(ao);
}
diff --git a/src/output_control.h b/src/output_control.h
index f0e317d6e..f58a113e6 100644
--- a/src/output_control.h
+++ b/src/output_control.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@ struct audio_output;
struct audio_format;
struct config_param;
struct music_pipe;
+struct player_control;
static inline GQuark
audio_output_quark(void)
@@ -38,6 +39,7 @@ audio_output_quark(void)
bool
audio_output_init(struct audio_output *ao, const struct config_param *param,
+ struct player_control *pc,
GError **error_r);
/**
diff --git a/src/output_finish.c b/src/output_finish.c
new file mode 100644
index 000000000..ac7f7e0c2
--- /dev/null
+++ b/src/output_finish.c
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "output_internal.h"
+#include "output_plugin.h"
+#include "mixer_control.h"
+#include "filter_plugin.h"
+
+#include <assert.h>
+
+void
+audio_output_destruct(struct audio_output *ao)
+{
+ assert(!ao->open);
+ assert(ao->fail_timer == NULL);
+ assert(ao->thread == NULL);
+
+ if (ao->mixer != NULL)
+ mixer_free(ao->mixer);
+
+ ao_plugin_finish(ao->plugin, ao->data);
+
+ g_cond_free(ao->cond);
+ g_mutex_free(ao->mutex);
+
+ if (ao->replay_gain_filter != NULL)
+ filter_free(ao->replay_gain_filter);
+
+ if (ao->other_replay_gain_filter != NULL)
+ filter_free(ao->other_replay_gain_filter);
+
+ filter_free(ao->filter);
+
+ pcm_buffer_deinit(&ao->cross_fade_buffer);
+}
diff --git a/src/output_init.c b/src/output_init.c
index 96f87f512..c52dcc8cd 100644
--- a/src/output_init.c
+++ b/src/output_init.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -127,8 +127,12 @@ audio_output_load_mixer(void *ao, const struct config_param *param,
bool
audio_output_init(struct audio_output *ao, const struct config_param *param,
+ struct player_control *pc,
GError **error_r)
{
+ assert(ao != NULL);
+ assert(pc != NULL);
+
const struct audio_output_plugin *plugin = NULL;
GError *error = NULL;
@@ -250,6 +254,7 @@ audio_output_init(struct audio_output *ao, const struct config_param *param,
ao->command = AO_COMMAND_NONE;
ao->mutex = g_mutex_new();
ao->cond = g_cond_new();
+ ao->player_control = pc;
ao->data = ao_plugin_init(plugin,
&ao->config_audio_format,
diff --git a/src/output_internal.h b/src/output_internal.h
index 7102ea5cd..eba3aed91 100644
--- a/src/output_internal.h
+++ b/src/output_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -217,6 +217,12 @@ struct audio_output {
GCond *cond;
/**
+ * The player_control object which "owns" this output. This
+ * object is needed to signal command completion.
+ */
+ struct player_control *player_control;
+
+ /**
* The #music_chunk which is currently being played. All
* chunks before this one may be returned to the
* #music_buffer, because they are not going to be used by
@@ -248,4 +254,7 @@ audio_output_command_is_finished(const struct audio_output *ao)
return ao->command == AO_COMMAND_NONE;
}
+void
+audio_output_destruct(struct audio_output *ao);
+
#endif
diff --git a/src/output_list.c b/src/output_list.c
index 8238f581b..75d24d63b 100644
--- a/src/output_list.c
+++ b/src/output_list.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -26,10 +26,12 @@ extern const struct audio_output_plugin null_output_plugin;
extern const struct audio_output_plugin fifo_output_plugin;
extern const struct audio_output_plugin pipe_output_plugin;
extern const struct audio_output_plugin alsaPlugin;
+extern const struct audio_output_plugin roar_output_plugin;
extern const struct audio_output_plugin ao_output_plugin;
extern const struct audio_output_plugin oss_output_plugin;
extern const struct audio_output_plugin openal_output_plugin;
extern const struct audio_output_plugin osxPlugin;
+extern const struct audio_output_plugin raopPlugin;
extern const struct audio_output_plugin solaris_output_plugin;
extern const struct audio_output_plugin pulse_output_plugin;
extern const struct audio_output_plugin mvp_output_plugin;
@@ -53,6 +55,9 @@ const struct audio_output_plugin *audio_output_plugins[] = {
#ifdef HAVE_ALSA
&alsaPlugin,
#endif
+#ifdef HAVE_ROAR
+ &roar_output_plugin,
+#endif
#ifdef HAVE_AO
&ao_output_plugin,
#endif
@@ -65,6 +70,9 @@ const struct audio_output_plugin *audio_output_plugins[] = {
#ifdef HAVE_OSX
&osxPlugin,
#endif
+#ifdef ENABLE_RAOP_OUTPUT
+ &raopPlugin,
+#endif
#ifdef ENABLE_SOLARIS_OUTPUT
&solaris_output_plugin,
#endif
diff --git a/src/output_list.h b/src/output_list.h
index d72bc224b..3deb31c00 100644
--- a/src/output_list.h
+++ b/src/output_list.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_plugin.h b/src/output_plugin.h
index 36e17ed1b..72b519d13 100644
--- a/src/output_plugin.h
+++ b/src/output_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -52,7 +52,7 @@ struct audio_output_plugin {
* none is configured
* @param param the configuration section, or NULL if there is
* no configuration
- * @param error location to store the error occuring, or NULL
+ * @param error location to store the error occurring, or NULL
* to ignore errors
* @return NULL on error, or an opaque pointer to the plugin's
* data
@@ -72,7 +72,7 @@ struct audio_output_plugin {
* fail: if an error occurs during that, it should be reported
* by the open() method.
*
- * @param error_r location to store the error occuring, or
+ * @param error_r location to store the error occurring, or
* NULL to ignore errors
* @return true on success, false on error
*/
@@ -89,7 +89,7 @@ struct audio_output_plugin {
*
* @param audio_format the audio format in which data is going
* to be delivered; may be modified by the plugin
- * @param error location to store the error occuring, or NULL
+ * @param error location to store the error occurring, or NULL
* to ignore errors
*/
bool (*open)(void *data, struct audio_format *audio_format,
@@ -119,7 +119,7 @@ struct audio_output_plugin {
/**
* Play a chunk of audio data.
*
- * @param error location to store the error occuring, or NULL
+ * @param error location to store the error occurring, or NULL
* to ignore errors
* @return the number of bytes played, or 0 on error
*/
diff --git a/src/output_print.c b/src/output_print.c
index 7a747ad2f..483648ca2 100644
--- a/src/output_print.c
+++ b/src/output_print.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_print.h b/src/output_print.h
index 5ad7e34c7..e02f4e9f5 100644
--- a/src/output_print.h
+++ b/src/output_print.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_state.c b/src/output_state.c
index e1187b951..7bcafb36b 100644
--- a/src/output_state.c
+++ b/src/output_state.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_state.h b/src/output_state.h
index 962ccd97a..320a3520a 100644
--- a/src/output_state.h
+++ b/src/output_state.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/output_thread.c b/src/output_thread.c
index bf56ca971..c36ba5f4f 100644
--- a/src/output_thread.c
+++ b/src/output_thread.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
#include "filter/convert_filter_plugin.h"
#include "filter/replay_gain_filter_plugin.h"
#include "mpd_error.h"
+#include "notify.h"
#include <glib.h>
@@ -535,7 +536,7 @@ ao_play(struct audio_output *ao)
ao->chunk_finished = true;
g_mutex_unlock(ao->mutex);
- player_lock_signal();
+ player_lock_signal(ao->player_control);
g_mutex_lock(ao->mutex);
return true;
diff --git a/src/output_thread.h b/src/output_thread.h
index 1ee0856f2..5ad9a7527 100644
--- a/src/output_thread.h
+++ b/src/output_thread.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/page.c b/src/page.c
index 59369cb34..e2e22791f 100644
--- a/src/page.c
+++ b/src/page.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/page.h b/src/page.h
index 652c4ad6e..8a3aaf396 100644
--- a/src/page.h
+++ b/src/page.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/path.c b/src/path.c
index 5e39c1636..42e5a5c2a 100644
--- a/src/path.c
+++ b/src/path.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/path.h b/src/path.h
index 512cd13ea..00c368e70 100644
--- a/src/path.h
+++ b/src/path.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_buffer.h b/src/pcm_buffer.h
index 73959ea03..1013e87a6 100644
--- a/src/pcm_buffer.h
+++ b/src/pcm_buffer.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_byteswap.c b/src/pcm_byteswap.c
index 6577319d4..4e604c79a 100644
--- a/src/pcm_byteswap.c
+++ b/src/pcm_byteswap.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_byteswap.h b/src/pcm_byteswap.h
index 005e75ded..1098a2fe1 100644
--- a/src/pcm_byteswap.h
+++ b/src/pcm_byteswap.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_channels.c b/src/pcm_channels.c
index 34e72ca4e..05a65a62a 100644
--- a/src/pcm_channels.c
+++ b/src/pcm_channels.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_channels.h b/src/pcm_channels.h
index a23cbd364..ee27697b7 100644
--- a/src/pcm_channels.h
+++ b/src/pcm_channels.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_convert.c b/src/pcm_convert.c
index 5fe89b53a..59dc0d5c4 100644
--- a/src/pcm_convert.c
+++ b/src/pcm_convert.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_convert.h b/src/pcm_convert.h
index 01ba2c787..6dbd7541b 100644
--- a/src/pcm_convert.h
+++ b/src/pcm_convert.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -75,7 +75,7 @@ void pcm_convert_deinit(struct pcm_convert_state *state);
* @param src_size the size of #src in bytes
* @param dest_format the requested destination audio format
* @param dest_size_r returns the number of bytes of the destination buffer
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
* @return the destination buffer, or NULL on error
*/
diff --git a/src/pcm_dither.c b/src/pcm_dither.c
index 03388f0e0..a75b07e5d 100644
--- a/src/pcm_dither.c
+++ b/src/pcm_dither.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_dither.h b/src/pcm_dither.h
index dafae957f..5ba4d7bb4 100644
--- a/src/pcm_dither.h
+++ b/src/pcm_dither.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_format.c b/src/pcm_format.c
index 1e4b8d705..544b0ccd9 100644
--- a/src/pcm_format.c
+++ b/src/pcm_format.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_format.h b/src/pcm_format.h
index 3e96fc65f..54cc32020 100644
--- a/src/pcm_format.h
+++ b/src/pcm_format.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_mix.c b/src/pcm_mix.c
index 3145c07be..8cdad2c11 100644
--- a/src/pcm_mix.c
+++ b/src/pcm_mix.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_mix.h b/src/pcm_mix.h
index 086d5501e..9b576f40d 100644
--- a/src/pcm_mix.h
+++ b/src/pcm_mix.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_pack.c b/src/pcm_pack.c
index 9af0ab1ed..680d2a32f 100644
--- a/src/pcm_pack.c
+++ b/src/pcm_pack.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_pack.h b/src/pcm_pack.h
index 3c99eaa35..0055e91df 100644
--- a/src/pcm_pack.h
+++ b/src/pcm_pack.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_prng.h b/src/pcm_prng.h
index 186ed9d0e..457ba4b66 100644
--- a/src/pcm_prng.h
+++ b/src/pcm_prng.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_resample.c b/src/pcm_resample.c
index 4a7578e09..a1e4ee149 100644
--- a/src/pcm_resample.c
+++ b/src/pcm_resample.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_resample.h b/src/pcm_resample.h
index 24d17ff9b..167f15e8c 100644
--- a/src/pcm_resample.h
+++ b/src/pcm_resample.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_resample_fallback.c b/src/pcm_resample_fallback.c
index 0c75d8ba4..79c2f5176 100644
--- a/src/pcm_resample_fallback.c
+++ b/src/pcm_resample_fallback.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_resample_internal.h b/src/pcm_resample_internal.h
index 26acc809d..37aae96ef 100644
--- a/src/pcm_resample_internal.h
+++ b/src/pcm_resample_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_resample_libsamplerate.c b/src/pcm_resample_libsamplerate.c
index 99ca53da4..ebce488f4 100644
--- a/src/pcm_resample_libsamplerate.c
+++ b/src/pcm_resample_libsamplerate.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_utils.h b/src/pcm_utils.h
index 15f9e1b10..b6a6f3787 100644
--- a/src/pcm_utils.h
+++ b/src/pcm_utils.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_volume.c b/src/pcm_volume.c
index 240c779d8..69c239cb8 100644
--- a/src/pcm_volume.c
+++ b/src/pcm_volume.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pcm_volume.h b/src/pcm_volume.h
index eb61e9526..b389dee3c 100644
--- a/src/pcm_volume.h
+++ b/src/pcm_volume.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/permission.c b/src/permission.c
index 17b443e0a..cd52b9c86 100644
--- a/src/permission.c
+++ b/src/permission.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/permission.h b/src/permission.h
index 9b3a60a66..6c3771362 100644
--- a/src/permission.h
+++ b/src/permission.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pipe.c b/src/pipe.c
index 2f5f70e43..d8131432f 100644
--- a/src/pipe.c
+++ b/src/pipe.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/pipe.h b/src/pipe.h
index efa7a84f0..84b9869e0 100644
--- a/src/pipe.h
+++ b/src/pipe.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/player_control.c b/src/player_control.c
index a190bbd8b..51420a43f 100644
--- a/src/player_control.c
+++ b/src/player_control.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -32,237 +32,247 @@
#include <stdio.h>
#include <math.h>
-struct player_control pc;
-
static void
-pc_enqueue_song_locked(struct song *song);
+pc_enqueue_song_locked(struct player_control *pc, struct song *song);
-void pc_init(unsigned buffer_chunks, unsigned int buffered_before_play)
+struct player_control *
+pc_new(unsigned buffer_chunks, unsigned int buffered_before_play)
{
- pc.buffer_chunks = buffer_chunks;
- pc.buffered_before_play = buffered_before_play;
-
- pc.mutex = g_mutex_new();
- pc.cond = g_cond_new();
-
- pc.command = PLAYER_COMMAND_NONE;
- pc.error = PLAYER_ERROR_NOERROR;
- pc.state = PLAYER_STATE_STOP;
- pc.cross_fade_seconds = 0;
- pc.mixramp_db = 0;
- pc.mixramp_delay_seconds = nanf("");
+ struct player_control *pc = g_new0(struct player_control, 1);
+
+ pc->buffer_chunks = buffer_chunks;
+ pc->buffered_before_play = buffered_before_play;
+
+ pc->mutex = g_mutex_new();
+ pc->cond = g_cond_new();
+
+ pc->command = PLAYER_COMMAND_NONE;
+ pc->error = PLAYER_ERROR_NOERROR;
+ pc->state = PLAYER_STATE_STOP;
+ pc->cross_fade_seconds = 0;
+ pc->mixramp_db = 0;
+ pc->mixramp_delay_seconds = nanf("");
+
+ return pc;
}
-void pc_deinit(void)
+void
+pc_free(struct player_control *pc)
{
- g_cond_free(pc.cond);
- g_mutex_free(pc.mutex);
+ g_cond_free(pc->cond);
+ g_mutex_free(pc->mutex);
+ g_free(pc);
}
void
-player_wait_decoder(struct decoder_control *dc)
+player_wait_decoder(struct player_control *pc, struct decoder_control *dc)
{
+ assert(pc != NULL);
+ assert(dc != NULL);
+ assert(dc->client_cond == pc->cond);
+
/* during this function, the decoder lock is held, because
we're waiting for the decoder thread */
- g_cond_wait(pc.cond, dc->mutex);
+ g_cond_wait(pc->cond, dc->mutex);
}
void
-pc_song_deleted(const struct song *song)
+pc_song_deleted(struct player_control *pc, const struct song *song)
{
- if (pc.errored_song == song) {
- pc.error = PLAYER_ERROR_NOERROR;
- pc.errored_song = NULL;
+ if (pc->errored_song == song) {
+ pc->error = PLAYER_ERROR_NOERROR;
+ pc->errored_song = NULL;
}
}
static void
-player_command_wait_locked(void)
+player_command_wait_locked(struct player_control *pc)
{
- while (pc.command != PLAYER_COMMAND_NONE)
- g_cond_wait(main_cond, pc.mutex);
+ while (pc->command != PLAYER_COMMAND_NONE)
+ g_cond_wait(main_cond, pc->mutex);
}
static void
-player_command_locked(enum player_command cmd)
+player_command_locked(struct player_control *pc, enum player_command cmd)
{
- assert(pc.command == PLAYER_COMMAND_NONE);
+ assert(pc->command == PLAYER_COMMAND_NONE);
- pc.command = cmd;
- player_signal();
- player_command_wait_locked();
+ pc->command = cmd;
+ player_signal(pc);
+ player_command_wait_locked(pc);
}
static void
-player_command(enum player_command cmd)
+player_command(struct player_control *pc, enum player_command cmd)
{
- player_lock();
- player_command_locked(cmd);
- player_unlock();
+ player_lock(pc);
+ player_command_locked(pc, cmd);
+ player_unlock(pc);
}
void
-pc_play(struct song *song)
+pc_play(struct player_control *pc, struct song *song)
{
assert(song != NULL);
- player_lock();
+ player_lock(pc);
- if (pc.state != PLAYER_STATE_STOP)
- player_command_locked(PLAYER_COMMAND_STOP);
+ if (pc->state != PLAYER_STATE_STOP)
+ player_command_locked(pc, PLAYER_COMMAND_STOP);
- assert(pc.next_song == NULL);
+ assert(pc->next_song == NULL);
- pc_enqueue_song_locked(song);
+ pc_enqueue_song_locked(pc, song);
- assert(pc.next_song == NULL);
+ assert(pc->next_song == NULL);
- player_unlock();
+ player_unlock(pc);
idle_add(IDLE_PLAYER);
}
-void pc_cancel(void)
+void
+pc_cancel(struct player_control *pc)
{
- player_command(PLAYER_COMMAND_CANCEL);
- assert(pc.next_song == NULL);
+ player_command(pc, PLAYER_COMMAND_CANCEL);
+ assert(pc->next_song == NULL);
}
void
-pc_stop(void)
+pc_stop(struct player_control *pc)
{
- player_command(PLAYER_COMMAND_CLOSE_AUDIO);
- assert(pc.next_song == NULL);
+ player_command(pc, PLAYER_COMMAND_CLOSE_AUDIO);
+ assert(pc->next_song == NULL);
idle_add(IDLE_PLAYER);
}
void
-pc_update_audio(void)
+pc_update_audio(struct player_control *pc)
{
- player_command(PLAYER_COMMAND_UPDATE_AUDIO);
+ player_command(pc, PLAYER_COMMAND_UPDATE_AUDIO);
}
void
-pc_kill(void)
+pc_kill(struct player_control *pc)
{
- assert(pc.thread != NULL);
+ assert(pc->thread != NULL);
- player_command(PLAYER_COMMAND_EXIT);
- g_thread_join(pc.thread);
- pc.thread = NULL;
+ player_command(pc, PLAYER_COMMAND_EXIT);
+ g_thread_join(pc->thread);
+ pc->thread = NULL;
idle_add(IDLE_PLAYER);
}
void
-pc_pause(void)
+pc_pause(struct player_control *pc)
{
- player_lock();
+ player_lock(pc);
- if (pc.state != PLAYER_STATE_STOP) {
- player_command_locked(PLAYER_COMMAND_PAUSE);
+ if (pc->state != PLAYER_STATE_STOP) {
+ player_command_locked(pc, PLAYER_COMMAND_PAUSE);
idle_add(IDLE_PLAYER);
}
- player_unlock();
+ player_unlock(pc);
}
static void
-pc_pause_locked(void)
+pc_pause_locked(struct player_control *pc)
{
- if (pc.state != PLAYER_STATE_STOP) {
- player_command_locked(PLAYER_COMMAND_PAUSE);
+ if (pc->state != PLAYER_STATE_STOP) {
+ player_command_locked(pc, PLAYER_COMMAND_PAUSE);
idle_add(IDLE_PLAYER);
}
}
void
-pc_set_pause(bool pause_flag)
+pc_set_pause(struct player_control *pc, bool pause_flag)
{
- player_lock();
+ player_lock(pc);
- switch (pc.state) {
+ switch (pc->state) {
case PLAYER_STATE_STOP:
break;
case PLAYER_STATE_PLAY:
if (pause_flag)
- pc_pause_locked();
+ pc_pause_locked(pc);
break;
case PLAYER_STATE_PAUSE:
if (!pause_flag)
- pc_pause_locked();
+ pc_pause_locked(pc);
break;
}
- player_unlock();
+ player_unlock(pc);
}
void
-pc_get_status(struct player_status *status)
+pc_get_status(struct player_control *pc, struct player_status *status)
{
- player_lock();
- player_command_locked(PLAYER_COMMAND_REFRESH);
+ player_lock(pc);
+ player_command_locked(pc, PLAYER_COMMAND_REFRESH);
- status->state = pc.state;
+ status->state = pc->state;
- if (pc.state != PLAYER_STATE_STOP) {
- status->bit_rate = pc.bit_rate;
- status->audio_format = pc.audio_format;
- status->total_time = pc.total_time;
- status->elapsed_time = pc.elapsed_time;
+ if (pc->state != PLAYER_STATE_STOP) {
+ status->bit_rate = pc->bit_rate;
+ status->audio_format = pc->audio_format;
+ status->total_time = pc->total_time;
+ status->elapsed_time = pc->elapsed_time;
}
- player_unlock();
+ player_unlock(pc);
}
enum player_state
-pc_get_state(void)
+pc_get_state(struct player_control *pc)
{
- return pc.state;
+ return pc->state;
}
void
-pc_clear_error(void)
+pc_clear_error(struct player_control *pc)
{
- player_lock();
- pc.error = PLAYER_ERROR_NOERROR;
- pc.errored_song = NULL;
- player_unlock();
+ player_lock(pc);
+ pc->error = PLAYER_ERROR_NOERROR;
+ pc->errored_song = NULL;
+ player_unlock(pc);
}
enum player_error
-pc_get_error(void)
+pc_get_error(struct player_control *pc)
{
- return pc.error;
+ return pc->error;
}
static char *
-pc_errored_song_uri(void)
+pc_errored_song_uri(struct player_control *pc)
{
- return song_get_uri(pc.errored_song);
+ return song_get_uri(pc->errored_song);
}
char *
-pc_get_error_message(void)
+pc_get_error_message(struct player_control *pc)
{
char *error;
char *uri;
- switch (pc.error) {
+ switch (pc->error) {
case PLAYER_ERROR_NOERROR:
return NULL;
case PLAYER_ERROR_FILENOTFOUND:
- uri = pc_errored_song_uri();
+ uri = pc_errored_song_uri(pc);
error = g_strdup_printf("file \"%s\" does not exist or is inaccessible", uri);
g_free(uri);
return error;
case PLAYER_ERROR_FILE:
- uri = pc_errored_song_uri();
+ uri = pc_errored_song_uri(pc);
error = g_strdup_printf("problems decoding \"%s\"", uri);
g_free(uri);
return error;
@@ -271,10 +281,10 @@ pc_get_error_message(void)
return g_strdup("problems opening audio device");
case PLAYER_ERROR_SYSTEM:
- return g_strdup("system error occured");
+ return g_strdup("system error occurred");
case PLAYER_ERROR_UNKTYPE:
- uri = pc_errored_song_uri();
+ uri = pc_errored_song_uri(pc);
error = g_strdup_printf("file type of \"%s\" is unknown", uri);
g_free(uri);
return error;
@@ -285,40 +295,40 @@ pc_get_error_message(void)
}
static void
-pc_enqueue_song_locked(struct song *song)
+pc_enqueue_song_locked(struct player_control *pc, struct song *song)
{
assert(song != NULL);
- assert(pc.next_song == NULL);
+ assert(pc->next_song == NULL);
- pc.next_song = song;
- player_command_locked(PLAYER_COMMAND_QUEUE);
+ pc->next_song = song;
+ player_command_locked(pc, PLAYER_COMMAND_QUEUE);
}
void
-pc_enqueue_song(struct song *song)
+pc_enqueue_song(struct player_control *pc, struct song *song)
{
assert(song != NULL);
- player_lock();
- pc_enqueue_song_locked(song);
- player_unlock();
+ player_lock(pc);
+ pc_enqueue_song_locked(pc, song);
+ player_unlock(pc);
}
bool
-pc_seek(struct song *song, float seek_time)
+pc_seek(struct player_control *pc, struct song *song, float seek_time)
{
assert(song != NULL);
- if (pc.state == PLAYER_STATE_STOP)
+ if (pc->state == PLAYER_STATE_STOP)
return false;
- player_lock();
- pc.next_song = song;
- pc.seek_where = seek_time;
- player_command_locked(PLAYER_COMMAND_SEEK);
- player_unlock();
+ player_lock(pc);
+ pc->next_song = song;
+ pc->seek_where = seek_time;
+ player_command_locked(pc, PLAYER_COMMAND_SEEK);
+ player_unlock(pc);
- assert(pc.next_song == NULL);
+ assert(pc->next_song == NULL);
idle_add(IDLE_PLAYER);
@@ -326,51 +336,51 @@ pc_seek(struct song *song, float seek_time)
}
float
-pc_get_cross_fade(void)
+pc_get_cross_fade(const struct player_control *pc)
{
- return pc.cross_fade_seconds;
+ return pc->cross_fade_seconds;
}
void
-pc_set_cross_fade(float cross_fade_seconds)
+pc_set_cross_fade(struct player_control *pc, float cross_fade_seconds)
{
if (cross_fade_seconds < 0)
cross_fade_seconds = 0;
- pc.cross_fade_seconds = cross_fade_seconds;
+ pc->cross_fade_seconds = cross_fade_seconds;
idle_add(IDLE_OPTIONS);
}
float
-pc_get_mixramp_db(void)
+pc_get_mixramp_db(const struct player_control *pc)
{
- return pc.mixramp_db;
+ return pc->mixramp_db;
}
void
-pc_set_mixramp_db(float mixramp_db)
+pc_set_mixramp_db(struct player_control *pc, float mixramp_db)
{
- pc.mixramp_db = mixramp_db;
+ pc->mixramp_db = mixramp_db;
idle_add(IDLE_OPTIONS);
}
float
-pc_get_mixramp_delay(void)
+pc_get_mixramp_delay(const struct player_control *pc)
{
- return pc.mixramp_delay_seconds;
+ return pc->mixramp_delay_seconds;
}
void
-pc_set_mixramp_delay(float mixramp_delay_seconds)
+pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds)
{
- pc.mixramp_delay_seconds = mixramp_delay_seconds;
+ pc->mixramp_delay_seconds = mixramp_delay_seconds;
idle_add(IDLE_OPTIONS);
}
double
-pc_get_total_play_time(void)
+pc_get_total_play_time(const struct player_control *pc)
{
- return pc.total_play_time;
+ return pc->total_play_time;
}
diff --git a/src/player_control.h b/src/player_control.h
index 76c47609a..5a04ab0f9 100644
--- a/src/player_control.h
+++ b/src/player_control.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,9 +20,10 @@
#ifndef MPD_PLAYER_H
#define MPD_PLAYER_H
-#include "notify.h"
#include "audio_format.h"
+#include <glib.h>
+
#include <stdint.h>
struct decoder_control;
@@ -116,28 +117,28 @@ struct player_control {
double total_play_time;
};
-extern struct player_control pc;
-
-void pc_init(unsigned buffer_chunks, unsigned buffered_before_play);
+struct player_control *
+pc_new(unsigned buffer_chunks, unsigned buffered_before_play);
-void pc_deinit(void);
+void
+pc_free(struct player_control *pc);
/**
* Locks the #player_control object.
*/
static inline void
-player_lock(void)
+player_lock(struct player_control *pc)
{
- g_mutex_lock(pc.mutex);
+ g_mutex_lock(pc->mutex);
}
/**
* Unlocks the #player_control object.
*/
static inline void
-player_unlock(void)
+player_unlock(struct player_control *pc)
{
- g_mutex_unlock(pc.mutex);
+ g_mutex_unlock(pc->mutex);
}
/**
@@ -146,9 +147,9 @@ player_unlock(void)
* to calling this function.
*/
static inline void
-player_wait(void)
+player_wait(struct player_control *pc)
{
- g_cond_wait(pc.cond, pc.mutex);
+ g_cond_wait(pc->cond, pc->mutex);
}
/**
@@ -159,16 +160,16 @@ player_wait(void)
* Note the small difference to the player_wait() function!
*/
void
-player_wait_decoder(struct decoder_control *dc);
+player_wait_decoder(struct player_control *pc, struct decoder_control *dc);
/**
* Signals the #player_control object. The object should be locked
* prior to calling this function.
*/
static inline void
-player_signal(void)
+player_signal(struct player_control *pc)
{
- g_cond_signal(pc.cond);
+ g_cond_signal(pc->cond);
}
/**
@@ -176,11 +177,11 @@ player_signal(void)
* locked by this function.
*/
static inline void
-player_lock_signal(void)
+player_lock_signal(struct player_control *pc)
{
- player_lock();
- player_signal();
- player_unlock();
+ player_lock(pc);
+ player_signal(pc);
+ player_unlock(pc);
}
/**
@@ -189,33 +190,34 @@ player_lock_signal(void)
* not point to an invalid pointer.
*/
void
-pc_song_deleted(const struct song *song);
+pc_song_deleted(struct player_control *pc, const struct song *song);
void
-pc_play(struct song *song);
+pc_play(struct player_control *pc, struct song *song);
/**
* see PLAYER_COMMAND_CANCEL
*/
-void pc_cancel(void);
+void
+pc_cancel(struct player_control *pc);
void
-pc_set_pause(bool pause_flag);
+pc_set_pause(struct player_control *pc, bool pause_flag);
void
-pc_pause(void);
+pc_pause(struct player_control *pc);
void
-pc_kill(void);
+pc_kill(struct player_control *pc);
void
-pc_get_status(struct player_status *status);
+pc_get_status(struct player_control *pc, struct player_status *status);
enum player_state
-pc_get_state(void);
+pc_get_state(struct player_control *pc);
void
-pc_clear_error(void);
+pc_clear_error(struct player_control *pc);
/**
* Returns the human-readable message describing the last error during
@@ -223,19 +225,19 @@ pc_clear_error(void);
* returned string.
*/
char *
-pc_get_error_message(void);
+pc_get_error_message(struct player_control *pc);
enum player_error
-pc_get_error(void);
+pc_get_error(struct player_control *pc);
void
-pc_stop(void);
+pc_stop(struct player_control *pc);
void
-pc_update_audio(void);
+pc_update_audio(struct player_control *pc);
void
-pc_enqueue_song(struct song *song);
+pc_enqueue_song(struct player_control *pc, struct song *song);
/**
* Makes the player thread seek the specified song to a position.
@@ -244,27 +246,27 @@ pc_enqueue_song(struct song *song);
* playing currently)
*/
bool
-pc_seek(struct song *song, float seek_time);
+pc_seek(struct player_control *pc, struct song *song, float seek_time);
void
-pc_set_cross_fade(float cross_fade_seconds);
+pc_set_cross_fade(struct player_control *pc, float cross_fade_seconds);
float
-pc_get_cross_fade(void);
+pc_get_cross_fade(const struct player_control *pc);
void
-pc_set_mixramp_db(float mixramp_db);
+pc_set_mixramp_db(struct player_control *pc, float mixramp_db);
float
-pc_get_mixramp_db(void);
+pc_get_mixramp_db(const struct player_control *pc);
void
-pc_set_mixramp_delay(float mixramp_delay_seconds);
+pc_set_mixramp_delay(struct player_control *pc, float mixramp_delay_seconds);
float
-pc_get_mixramp_delay(void);
+pc_get_mixramp_delay(const struct player_control *pc);
double
-pc_get_total_play_time(void);
+pc_get_total_play_time(const struct player_control *pc);
#endif
diff --git a/src/player_thread.c b/src/player_thread.c
index a89e59908..58682f2ca 100644
--- a/src/player_thread.c
+++ b/src/player_thread.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -48,6 +48,8 @@ enum xfade_state {
};
struct player {
+ struct player_control *pc;
+
struct decoder_control *dc;
struct music_pipe *pipe;
@@ -110,26 +112,28 @@ struct player {
* output thread. This attribute is only used if
* audio_output_all_get_elapsed_time() didn't return a usable
* value; the output thread can estimate the elapsed time more
- * precisly.
+ * precisely.
*/
float elapsed_time;
};
static struct music_buffer *player_buffer;
-static void player_command_finished_locked(void)
+static void
+player_command_finished_locked(struct player_control *pc)
{
- assert(pc.command != PLAYER_COMMAND_NONE);
+ assert(pc->command != PLAYER_COMMAND_NONE);
- pc.command = PLAYER_COMMAND_NONE;
+ pc->command = PLAYER_COMMAND_NONE;
g_cond_signal(main_cond);
}
-static void player_command_finished(void)
+static void
+player_command_finished(struct player_control *pc)
{
- player_lock();
- player_command_finished_locked();
- player_unlock();
+ player_lock(pc);
+ player_command_finished_locked(pc);
+ player_unlock(pc);
}
/**
@@ -140,12 +144,13 @@ static void player_command_finished(void)
static void
player_dc_start(struct player *player, struct music_pipe *pipe)
{
+ struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc;
- assert(player->queued || pc.command == PLAYER_COMMAND_SEEK);
- assert(pc.next_song != NULL);
+ assert(player->queued || pc->command == PLAYER_COMMAND_SEEK);
+ assert(pc->next_song != NULL);
- dc_start(dc, pc.next_song, player_buffer, pipe);
+ dc_start(dc, pc->next_song, player_buffer, pipe);
}
/**
@@ -208,41 +213,42 @@ player_dc_stop(struct player *player)
static bool
player_wait_for_decoder(struct player *player)
{
+ struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc;
- assert(player->queued || pc.command == PLAYER_COMMAND_SEEK);
- assert(pc.next_song != NULL);
+ assert(player->queued || pc->command == PLAYER_COMMAND_SEEK);
+ assert(pc->next_song != NULL);
player->queued = false;
if (decoder_lock_has_failed(dc)) {
- player_lock();
- pc.errored_song = dc->song;
- pc.error = PLAYER_ERROR_FILE;
- pc.next_song = NULL;
- player_unlock();
+ player_lock(pc);
+ pc->errored_song = dc->song;
+ pc->error = PLAYER_ERROR_FILE;
+ pc->next_song = NULL;
+ player_unlock(pc);
return false;
}
- player->song = pc.next_song;
+ player->song = pc->next_song;
player->elapsed_time = 0.0;
/* set the "starting" flag, which will be cleared by
player_check_decoder_startup() */
player->decoder_starting = true;
- player_lock();
+ player_lock(pc);
/* update player_control's song information */
- pc.total_time = song_get_duration(pc.next_song);
- pc.bit_rate = 0;
- audio_format_clear(&pc.audio_format);
+ pc->total_time = song_get_duration(pc->next_song);
+ pc->bit_rate = 0;
+ audio_format_clear(&pc->audio_format);
/* clear the queued song */
- pc.next_song = NULL;
+ pc->next_song = NULL;
- player_unlock();
+ player_unlock(pc);
/* call syncPlaylistWithQueue() in the main thread */
event_pipe_emit(PIPE_EVENT_PLAYLIST);
@@ -280,6 +286,7 @@ real_song_duration(const struct song *song, double decoder_duration)
static bool
player_check_decoder_startup(struct player *player)
{
+ struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc;
assert(player->decoder_starting);
@@ -290,10 +297,10 @@ player_check_decoder_startup(struct player *player)
/* the decoder failed */
decoder_unlock(dc);
- player_lock();
- pc.errored_song = dc->song;
- pc.error = PLAYER_ERROR_FILE;
- player_unlock();
+ player_lock(pc);
+ pc->errored_song = dc->song;
+ pc->error = PLAYER_ERROR_FILE;
+ player_unlock(pc);
return false;
} else if (!decoder_is_starting(dc)) {
@@ -302,15 +309,15 @@ player_check_decoder_startup(struct player *player)
decoder_unlock(dc);
if (audio_format_defined(&player->play_audio_format) &&
- !audio_output_all_wait(1))
+ !audio_output_all_wait(pc, 1))
/* the output devices havn't finished playing
all chunks yet - wait for that */
return true;
- player_lock();
- pc.total_time = real_song_duration(dc->song, dc->total_time);
- pc.audio_format = dc->in_audio_format;
- player_unlock();
+ player_lock(pc);
+ pc->total_time = real_song_duration(dc->song, dc->total_time);
+ pc->audio_format = dc->in_audio_format;
+ player_unlock(pc);
player->play_audio_format = dc->out_audio_format;
player->decoder_starting = false;
@@ -323,13 +330,13 @@ player_check_decoder_startup(struct player *player)
"while playing \"%s\"", uri);
g_free(uri);
- player_lock();
- pc.error = PLAYER_ERROR_AUDIO;
+ player_lock(pc);
+ 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();
+ pc->state = PLAYER_STATE_PAUSE;
+ player_unlock(pc);
player->paused = true;
return true;
@@ -339,7 +346,7 @@ player_check_decoder_startup(struct player *player)
} else {
/* the decoder is not yet ready; wait
some more */
- player_wait_decoder(dc);
+ player_wait_decoder(pc, dc);
decoder_unlock(dc);
return true;
@@ -393,10 +400,11 @@ player_send_silence(struct player *player)
*/
static bool player_seek_decoder(struct player *player)
{
- struct song *song = pc.next_song;
+ struct player_control *pc = player->pc;
+ struct song *song = pc->next_song;
struct decoder_control *dc = player->dc;
- assert(pc.next_song != NULL);
+ assert(pc->next_song != NULL);
if (decoder_current_song(dc) != song) {
/* the decoder is already decoding the "next" song -
@@ -412,7 +420,7 @@ static bool player_seek_decoder(struct player *player)
player_dc_start(player, player->pipe);
if (!player_wait_for_decoder(player)) {
/* decoder failure */
- player_command_finished();
+ player_command_finished(pc);
return false;
}
} else {
@@ -424,7 +432,7 @@ static bool player_seek_decoder(struct player *player)
player->pipe = dc->pipe;
}
- pc.next_song = NULL;
+ pc->next_song = NULL;
player->queued = false;
}
@@ -433,28 +441,28 @@ static bool player_seek_decoder(struct player *player)
while (player->decoder_starting) {
if (!player_check_decoder_startup(player)) {
/* decoder failure */
- player_command_finished();
+ player_command_finished(pc);
return false;
}
}
/* send the SEEK command */
- double where = pc.seek_where;
- if (where > pc.total_time)
- where = pc.total_time - 0.1;
+ double where = pc->seek_where;
+ if (where > pc->total_time)
+ where = pc->total_time - 0.1;
if (where < 0.0)
where = 0.0;
if (!dc_seek(dc, where + song->start_ms / 1000.0)) {
/* decoder failure */
- player_command_finished();
+ player_command_finished(pc);
return false;
}
player->elapsed_time = where;
- player_command_finished();
+ player_command_finished(pc);
player->xfade = XFADE_UNKNOWN;
@@ -471,9 +479,10 @@ static bool player_seek_decoder(struct player *player)
*/
static void player_process_command(struct player *player)
{
+ struct player_control *pc = player->pc;
G_GNUC_UNUSED struct decoder_control *dc = player->dc;
- switch (pc.command) {
+ switch (pc->command) {
case PLAYER_COMMAND_NONE:
case PLAYER_COMMAND_STOP:
case PLAYER_COMMAND_EXIT:
@@ -481,95 +490,95 @@ static void player_process_command(struct player *player)
break;
case PLAYER_COMMAND_UPDATE_AUDIO:
- player_unlock();
+ player_unlock(pc);
audio_output_all_enable_disable();
- player_lock();
- player_command_finished_locked();
+ player_lock(pc);
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_QUEUE:
- assert(pc.next_song != NULL);
+ assert(pc->next_song != NULL);
assert(!player->queued);
assert(!player_dc_at_next_song(player));
player->queued = true;
- player_command_finished_locked();
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_PAUSE:
- player_unlock();
+ player_unlock(pc);
player->paused = !player->paused;
if (player->paused) {
audio_output_all_pause();
- player_lock();
+ player_lock(pc);
- pc.state = PLAYER_STATE_PAUSE;
+ pc->state = PLAYER_STATE_PAUSE;
} else if (!audio_format_defined(&player->play_audio_format)) {
/* the decoder hasn't provided an audio format
yet - don't open the audio device yet */
- player_lock();
+ player_lock(pc);
- pc.state = PLAYER_STATE_PLAY;
+ pc->state = PLAYER_STATE_PLAY;
} else if (audio_output_all_open(&player->play_audio_format, player_buffer)) {
/* unpaused, continue playing */
- player_lock();
+ player_lock(pc);
- pc.state = PLAYER_STATE_PLAY;
+ pc->state = PLAYER_STATE_PLAY;
} else {
/* the audio device has failed - rollback to
pause mode */
- pc.error = PLAYER_ERROR_AUDIO;
+ pc->error = PLAYER_ERROR_AUDIO;
player->paused = true;
- player_lock();
+ player_lock(pc);
}
- player_command_finished_locked();
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_SEEK:
- player_unlock();
+ player_unlock(pc);
player_seek_decoder(player);
- player_lock();
+ player_lock(pc);
break;
case PLAYER_COMMAND_CANCEL:
- if (pc.next_song == NULL) {
+ if (pc->next_song == NULL) {
/* the cancel request arrived too late, we're
already playing the queued song... stop
everything now */
- pc.command = PLAYER_COMMAND_STOP;
+ pc->command = PLAYER_COMMAND_STOP;
return;
}
if (player_dc_at_next_song(player)) {
/* the decoder is already decoding the song -
stop it and reset the position */
- player_unlock();
+ player_unlock(pc);
player_dc_stop(player);
- player_lock();
+ player_lock(pc);
}
- pc.next_song = NULL;
+ pc->next_song = NULL;
player->queued = false;
- player_command_finished_locked();
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_REFRESH:
if (audio_format_defined(&player->play_audio_format) &&
!player->paused) {
- player_unlock();
+ player_unlock(pc);
audio_output_all_check();
- player_lock();
+ player_lock(pc);
}
- pc.elapsed_time = audio_output_all_get_elapsed_time();
- if (pc.elapsed_time < 0.0)
- pc.elapsed_time = player->elapsed_time;
+ pc->elapsed_time = audio_output_all_get_elapsed_time();
+ if (pc->elapsed_time < 0.0)
+ pc->elapsed_time = player->elapsed_time;
- player_command_finished_locked();
+ player_command_finished_locked(pc);
break;
}
}
@@ -605,7 +614,8 @@ update_song_tag(struct song *song, const struct tag *new_tag)
* Player lock is not held.
*/
static bool
-play_chunk(struct song *song, struct music_chunk *chunk,
+play_chunk(struct player_control *pc,
+ struct song *song, struct music_chunk *chunk,
const struct audio_format *format)
{
assert(music_chunk_check_format(chunk, format));
@@ -618,16 +628,16 @@ play_chunk(struct song *song, struct music_chunk *chunk,
return true;
}
- player_lock();
- pc.bit_rate = chunk->bit_rate;
- player_unlock();
+ player_lock(pc);
+ pc->bit_rate = chunk->bit_rate;
+ player_unlock(pc);
/* send the chunk to the audio outputs */
if (!audio_output_all_play(chunk))
return false;
- pc.total_play_time += (double)chunk->length /
+ pc->total_play_time += (double)chunk->length /
audio_format_time_to_size(format);
return true;
}
@@ -641,9 +651,10 @@ play_chunk(struct song *song, struct music_chunk *chunk,
static bool
play_next_chunk(struct player *player)
{
+ struct player_control *pc = player->pc;
struct decoder_control *dc = player->dc;
- if (!audio_output_all_wait(64))
+ if (!audio_output_all_wait(pc, 64))
/* the output pipe is still large enough, don't send
another chunk */
return true;
@@ -680,7 +691,7 @@ play_next_chunk(struct player *player)
other_chunk->tag);
other_chunk->tag = NULL;
- if (isnan(pc.mixramp_delay_seconds)) {
+ if (isnan(pc->mixramp_delay_seconds)) {
chunk->mix_ratio = ((float)cross_fade_position)
/ player->cross_fade_chunks;
} else {
@@ -715,7 +726,7 @@ play_next_chunk(struct player *player)
} else {
/* wait for the decoder */
decoder_signal(dc);
- player_wait_decoder(dc);
+ player_wait_decoder(pc, dc);
decoder_unlock(dc);
return true;
@@ -738,19 +749,20 @@ play_next_chunk(struct player *player)
/* play the current chunk */
- if (!play_chunk(player->song, chunk, &player->play_audio_format)) {
+ if (!play_chunk(player->pc, player->song, chunk,
+ &player->play_audio_format)) {
music_buffer_return(player_buffer, chunk);
- player_lock();
+ player_lock(pc);
- pc.error = PLAYER_ERROR_AUDIO;
+ pc->error = PLAYER_ERROR_AUDIO;
/* pause: the user may resume playback as soon as an
audio output becomes available */
- pc.state = PLAYER_STATE_PAUSE;
+ pc->state = PLAYER_STATE_PAUSE;
player->paused = true;
- player_unlock();
+ player_unlock(pc);
return false;
}
@@ -760,7 +772,7 @@ play_next_chunk(struct player *player)
larger block at a time */
decoder_lock(dc);
if (!decoder_is_idle(dc) &&
- music_pipe_size(dc->pipe) <= (pc.buffered_before_play +
+ music_pipe_size(dc->pipe) <= (pc->buffered_before_play +
music_buffer_size(player_buffer) * 3) / 4)
decoder_signal(dc);
decoder_unlock(dc);
@@ -802,9 +814,10 @@ player_song_border(struct player *player)
* basically a state machine, which multiplexes data between the
* decoder thread and the output threads.
*/
-static void do_play(struct decoder_control *dc)
+static void do_play(struct player_control *pc, struct decoder_control *dc)
{
struct player player = {
+ .pc = pc,
.dc = dc,
.buffering = true,
.decoder_starting = false,
@@ -818,42 +831,42 @@ static void do_play(struct decoder_control *dc)
.elapsed_time = 0.0,
};
- player_unlock();
+ player_unlock(pc);
player.pipe = music_pipe_new();
player_dc_start(&player, player.pipe);
if (!player_wait_for_decoder(&player)) {
player_dc_stop(&player);
- player_command_finished();
+ player_command_finished(pc);
music_pipe_free(player.pipe);
event_pipe_emit(PIPE_EVENT_PLAYLIST);
- player_lock();
+ player_lock(pc);
return;
}
- player_lock();
- pc.state = PLAYER_STATE_PLAY;
- player_command_finished_locked();
+ player_lock(pc);
+ pc->state = PLAYER_STATE_PLAY;
+ player_command_finished_locked(pc);
while (true) {
player_process_command(&player);
- if (pc.command == PLAYER_COMMAND_STOP ||
- pc.command == PLAYER_COMMAND_EXIT ||
- pc.command == PLAYER_COMMAND_CLOSE_AUDIO) {
- player_unlock();
+ if (pc->command == PLAYER_COMMAND_STOP ||
+ pc->command == PLAYER_COMMAND_EXIT ||
+ pc->command == PLAYER_COMMAND_CLOSE_AUDIO) {
+ player_unlock(pc);
audio_output_all_cancel();
break;
}
- player_unlock();
+ player_unlock(pc);
if (player.buffering) {
/* buffering at the start of the song - wait
until the buffer is large enough, to
prevent stuttering on slow machines */
- if (music_pipe_size(player.pipe) < pc.buffered_before_play &&
+ if (music_pipe_size(player.pipe) < pc->buffered_before_play &&
!decoder_lock_is_idle(dc)) {
/* not enough decoded buffer space yet */
@@ -865,9 +878,9 @@ static void do_play(struct decoder_control *dc)
decoder_lock(dc);
/* XXX race condition: check decoder again */
- player_wait_decoder(dc);
+ player_wait_decoder(pc, dc);
decoder_unlock(dc);
- player_lock();
+ player_lock(pc);
continue;
} else {
/* buffering is complete */
@@ -891,7 +904,7 @@ static void do_play(struct decoder_control *dc)
!dc_seek(dc, song->start_ms / 1000.0))
player_dc_stop(&player);
- player_lock();
+ player_lock(pc);
continue;
}
@@ -920,9 +933,9 @@ static void do_play(struct decoder_control *dc)
calculate how many chunks will be required
for it */
player.cross_fade_chunks =
- cross_fade_calc(pc.cross_fade_seconds, dc->total_time,
- pc.mixramp_db,
- pc.mixramp_delay_seconds,
+ cross_fade_calc(pc->cross_fade_seconds, dc->total_time,
+ pc->mixramp_db,
+ pc->mixramp_delay_seconds,
dc->replay_gain_db,
dc->replay_gain_prev_db,
dc->mixramp_start,
@@ -930,7 +943,7 @@ static void do_play(struct decoder_control *dc)
&dc->out_audio_format,
&player.play_audio_format,
music_buffer_size(player_buffer) -
- pc.buffered_before_play);
+ pc->buffered_before_play);
if (player.cross_fade_chunks > 0) {
player.xfade = XFADE_ENABLED;
player.cross_fading = false;
@@ -941,10 +954,10 @@ static void do_play(struct decoder_control *dc)
}
if (player.paused) {
- player_lock();
+ player_lock(pc);
- if (pc.command == PLAYER_COMMAND_NONE)
- player_wait();
+ if (pc->command == PLAYER_COMMAND_NONE)
+ player_wait(pc);
continue;
} else if (!music_pipe_empty(player.pipe)) {
/* at least one music chunk is ready - send it
@@ -981,7 +994,7 @@ static void do_play(struct decoder_control *dc)
break;
}
- player_lock();
+ player_lock(pc);
}
player_dc_stop(&player);
@@ -992,113 +1005,115 @@ static void do_play(struct decoder_control *dc)
if (player.cross_fade_tag != NULL)
tag_free(player.cross_fade_tag);
- player_lock();
+ player_lock(pc);
if (player.queued) {
- assert(pc.next_song != NULL);
- pc.next_song = NULL;
+ assert(pc->next_song != NULL);
+ pc->next_song = NULL;
}
- pc.state = PLAYER_STATE_STOP;
+ pc->state = PLAYER_STATE_STOP;
- player_unlock();
+ player_unlock(pc);
event_pipe_emit(PIPE_EVENT_PLAYLIST);
- player_lock();
+ player_lock(pc);
}
-static gpointer player_task(G_GNUC_UNUSED gpointer arg)
+static gpointer
+player_task(gpointer arg)
{
- struct decoder_control dc;
+ struct player_control *pc = arg;
- dc_init(&dc);
- decoder_thread_start(&dc);
+ struct decoder_control *dc = dc_new(pc->cond);
+ decoder_thread_start(dc);
- player_buffer = music_buffer_new(pc.buffer_chunks);
+ player_buffer = music_buffer_new(pc->buffer_chunks);
- player_lock();
+ player_lock(pc);
while (1) {
- switch (pc.command) {
+ switch (pc->command) {
case PLAYER_COMMAND_QUEUE:
- assert(pc.next_song != NULL);
+ assert(pc->next_song != NULL);
- do_play(&dc);
+ do_play(pc, dc);
break;
case PLAYER_COMMAND_STOP:
- player_unlock();
+ player_unlock(pc);
audio_output_all_cancel();
- player_lock();
+ player_lock(pc);
/* fall through */
case PLAYER_COMMAND_SEEK:
case PLAYER_COMMAND_PAUSE:
- pc.next_song = NULL;
- player_command_finished_locked();
+ pc->next_song = NULL;
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_CLOSE_AUDIO:
- player_unlock();
+ player_unlock(pc);
audio_output_all_release();
- player_lock();
- player_command_finished_locked();
+ player_lock(pc);
+ player_command_finished_locked(pc);
#ifndef NDEBUG
/* in the DEBUG build, check for leaked
music_chunk objects by freeing the
music_buffer */
music_buffer_free(player_buffer);
- player_buffer = music_buffer_new(pc.buffer_chunks);
+ player_buffer = music_buffer_new(pc->buffer_chunks);
#endif
break;
case PLAYER_COMMAND_UPDATE_AUDIO:
- player_unlock();
+ player_unlock(pc);
audio_output_all_enable_disable();
- player_lock();
- player_command_finished_locked();
+ player_lock(pc);
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_EXIT:
- player_unlock();
+ player_unlock(pc);
- dc_quit(&dc);
- dc_deinit(&dc);
+ dc_quit(dc);
+ dc_free(dc);
audio_output_all_close();
music_buffer_free(player_buffer);
- player_command_finished();
+ player_command_finished(pc);
return NULL;
case PLAYER_COMMAND_CANCEL:
- pc.next_song = NULL;
- player_command_finished_locked();
+ pc->next_song = NULL;
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_REFRESH:
/* no-op when not playing */
- player_command_finished_locked();
+ player_command_finished_locked(pc);
break;
case PLAYER_COMMAND_NONE:
- player_wait();
+ player_wait(pc);
break;
}
}
}
-void player_create(void)
+void
+player_create(struct player_control *pc)
{
- assert(pc.thread == NULL);
+ assert(pc->thread == NULL);
GError *e = NULL;
- pc.thread = g_thread_create(player_task, NULL, true, &e);
- if (pc.thread == NULL)
+ pc->thread = g_thread_create(player_task, pc, true, &e);
+ if (pc->thread == NULL)
MPD_ERROR("Failed to spawn player task: %s", e->message);
}
diff --git a/src/player_thread.h b/src/player_thread.h
index e645b1d09..7373eb438 100644
--- a/src/player_thread.h
+++ b/src/player_thread.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -37,6 +37,9 @@
#ifndef MPD_PLAYER_THREAD_H
#define MPD_PLAYER_THREAD_H
-void player_create(void);
+struct player_control;
+
+void
+player_create(struct player_control *pc);
#endif
diff --git a/src/playlist.c b/src/playlist.c
index 4a1e54814..0c9eea92d 100644
--- a/src/playlist.c
+++ b/src/playlist.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -75,7 +75,8 @@ playlist_finish(struct playlist *playlist)
* Queue a song, addressed by its order number.
*/
static void
-playlist_queue_song_order(struct playlist *playlist, unsigned order)
+playlist_queue_song_order(struct playlist *playlist, struct player_control *pc,
+ unsigned order)
{
struct song *song;
char *uri;
@@ -89,16 +90,16 @@ playlist_queue_song_order(struct playlist *playlist, unsigned order)
g_debug("queue song %i:\"%s\"", playlist->queued, uri);
g_free(uri);
- pc_enqueue_song(song);
+ pc_enqueue_song(pc, song);
}
/**
* Called if the player thread has started playing the "queued" song.
*/
static void
-playlist_song_started(struct playlist *playlist)
+playlist_song_started(struct playlist *playlist, struct player_control *pc)
{
- assert(pc.next_song == NULL);
+ assert(pc->next_song == NULL);
assert(playlist->queued >= -1);
/* queued song has started: copy queued to current,
@@ -110,11 +111,13 @@ playlist_song_started(struct playlist *playlist)
/* Pause if we are in single mode. */
if(playlist->queue.single && !playlist->queue.repeat) {
- pc_set_pause(true);
+ pc_set_pause(pc, true);
}
if(playlist->queue.consume)
- playlist_delete(playlist, queue_order_to_position(&playlist->queue, current));
+ playlist_delete(playlist, pc,
+ queue_order_to_position(&playlist->queue,
+ current));
idle_add(IDLE_PLAYER);
}
@@ -129,7 +132,9 @@ playlist_get_queued_song(struct playlist *playlist)
}
void
-playlist_update_queued_song(struct playlist *playlist, const struct song *prev)
+playlist_update_queued_song(struct playlist *playlist,
+ struct player_control *pc,
+ const struct song *prev)
{
int next_order;
const struct song *next_song;
@@ -170,20 +175,21 @@ playlist_update_queued_song(struct playlist *playlist, const struct song *prev)
if (prev != NULL && next_song != prev) {
/* clear the currently queued song */
- pc_cancel();
+ pc_cancel(pc);
playlist->queued = -1;
}
if (next_order >= 0) {
if (next_song != prev)
- playlist_queue_song_order(playlist, next_order);
+ playlist_queue_song_order(playlist, pc, next_order);
else
playlist->queued = next_order;
}
}
void
-playlist_play_order(struct playlist *playlist, int orderNum)
+playlist_play_order(struct playlist *playlist, struct player_control *pc,
+ int orderNum)
{
struct song *song;
char *uri;
@@ -197,46 +203,46 @@ playlist_play_order(struct playlist *playlist, int orderNum)
g_debug("play %i:\"%s\"", orderNum, uri);
g_free(uri);
- pc_play(song);
+ pc_play(pc, song);
playlist->current = orderNum;
}
static void
-playlist_resume_playback(struct playlist *playlist);
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc);
/**
* This is the "PLAYLIST" event handler. It is invoked by the player
* thread whenever it requests a new queued song, or when it exits.
*/
void
-playlist_sync(struct playlist *playlist)
+playlist_sync(struct playlist *playlist, struct player_control *pc)
{
if (!playlist->playing)
/* this event has reached us out of sync: we aren't
playing anymore; ignore the event */
return;
- player_lock();
- enum player_state pc_state = pc_get_state();
- const struct song *pc_next_song = pc.next_song;
- player_unlock();
+ player_lock(pc);
+ enum player_state pc_state = pc_get_state(pc);
+ const struct song *pc_next_song = pc->next_song;
+ player_unlock(pc);
if (pc_state == PLAYER_STATE_STOP)
/* the player thread has stopped: check if playback
should be restarted with the next song. That can
happen if the playlist isn't filling the queue fast
enough */
- playlist_resume_playback(playlist);
+ playlist_resume_playback(playlist, pc);
else {
/* check if the player thread has already started
playing the queued song */
if (pc_next_song == NULL && playlist->queued != -1)
- playlist_song_started(playlist);
+ playlist_song_started(playlist, pc);
/* make sure the queued song is always set (if
possible) */
- if (pc.next_song == NULL && playlist->queued < 0)
- playlist_update_queued_song(playlist, NULL);
+ if (pc->next_song == NULL && playlist->queued < 0)
+ playlist_update_queued_song(playlist, pc, NULL);
}
}
@@ -245,14 +251,14 @@ playlist_sync(struct playlist *playlist)
* decide whether to re-start playback
*/
static void
-playlist_resume_playback(struct playlist *playlist)
+playlist_resume_playback(struct playlist *playlist, struct player_control *pc)
{
enum player_error error;
assert(playlist->playing);
- assert(pc_get_state() == PLAYER_STATE_STOP);
+ assert(pc_get_state(pc) == PLAYER_STATE_STOP);
- error = pc_get_error();
+ error = pc_get_error(pc);
if (error == PLAYER_ERROR_NOERROR)
playlist->error_count = 0;
else
@@ -263,10 +269,10 @@ playlist_resume_playback(struct playlist *playlist)
playlist->error_count >= queue_length(&playlist->queue))
/* too many errors, or critical error: stop
playback */
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
else
/* continue playback at the next song */
- playlist_next(playlist);
+ playlist_next(playlist, pc);
}
bool
@@ -294,7 +300,8 @@ playlist_get_consume(const struct playlist *playlist)
}
void
-playlist_set_repeat(struct playlist *playlist, bool status)
+playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
+ bool status)
{
if (status == playlist->queue.repeat)
return;
@@ -303,7 +310,7 @@ playlist_set_repeat(struct playlist *playlist, bool status)
/* if the last song is currently being played, the "next song"
might change when repeat mode is toggled */
- playlist_update_queued_song(playlist,
+ playlist_update_queued_song(playlist, pc,
playlist_get_queued_song(playlist));
idle_add(IDLE_OPTIONS);
@@ -321,7 +328,8 @@ playlist_order(struct playlist *playlist)
}
void
-playlist_set_single(struct playlist *playlist, bool status)
+playlist_set_single(struct playlist *playlist, struct player_control *pc,
+ bool status)
{
if (status == playlist->queue.single)
return;
@@ -330,7 +338,7 @@ playlist_set_single(struct playlist *playlist, bool status)
/* if the last song is currently being played, the "next song"
might change when single mode is toggled */
- playlist_update_queued_song(playlist,
+ playlist_update_queued_song(playlist, pc,
playlist_get_queued_song(playlist));
idle_add(IDLE_OPTIONS);
@@ -347,7 +355,8 @@ playlist_set_consume(struct playlist *playlist, bool status)
}
void
-playlist_set_random(struct playlist *playlist, bool status)
+playlist_set_random(struct playlist *playlist, struct player_control *pc,
+ bool status)
{
const struct song *queued;
@@ -384,7 +393,7 @@ playlist_set_random(struct playlist *playlist, bool status)
} else
playlist_order(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
idle_add(IDLE_OPTIONS);
}
diff --git a/src/playlist.h b/src/playlist.h
index 3ba90ff91..4c5f29e5d 100644
--- a/src/playlist.h
+++ b/src/playlist.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -21,24 +21,11 @@
#define MPD_PLAYLIST_H
#include "queue.h"
+#include "playlist_error.h"
#include <stdbool.h>
-#define PLAYLIST_COMMENT '#'
-
-enum playlist_result {
- PLAYLIST_RESULT_SUCCESS,
- PLAYLIST_RESULT_ERRNO,
- PLAYLIST_RESULT_DENIED,
- PLAYLIST_RESULT_NO_SUCH_SONG,
- PLAYLIST_RESULT_NO_SUCH_LIST,
- PLAYLIST_RESULT_LIST_EXISTS,
- PLAYLIST_RESULT_BAD_NAME,
- PLAYLIST_RESULT_BAD_RANGE,
- PLAYLIST_RESULT_NOT_PLAYING,
- PLAYLIST_RESULT_TOO_LARGE,
- PLAYLIST_RESULT_DISABLED,
-};
+struct player_control;
struct playlist {
/**
@@ -111,7 +98,7 @@ playlist_get_queue(const struct playlist *playlist)
}
void
-playlist_clear(struct playlist *playlist);
+playlist_clear(struct playlist *playlist, struct player_control *pc);
#ifndef WIN32
/**
@@ -119,20 +106,21 @@ playlist_clear(struct playlist *playlist);
* but only if the file's owner is equal to the specified uid.
*/
enum playlist_result
-playlist_append_file(struct playlist *playlist, const char *path, int uid,
- unsigned *added_id);
+playlist_append_file(struct playlist *playlist, struct player_control *pc,
+ const char *path, int uid, unsigned *added_id);
#endif
enum playlist_result
-playlist_append_uri(struct playlist *playlist, const char *file,
- unsigned *added_id);
+playlist_append_uri(struct playlist *playlist, struct player_control *pc,
+ const char *file, unsigned *added_id);
enum playlist_result
-playlist_append_song(struct playlist *playlist,
+playlist_append_song(struct playlist *playlist, struct player_control *pc,
struct song *song, unsigned *added_id);
enum playlist_result
-playlist_delete(struct playlist *playlist, unsigned song);
+playlist_delete(struct playlist *playlist, struct player_control *pc,
+ unsigned song);
/**
* Deletes a range of songs from the playlist.
@@ -141,64 +129,86 @@ playlist_delete(struct playlist *playlist, unsigned song);
* @param end the position after the last song to delete
*/
enum playlist_result
-playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end);
+playlist_delete_range(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end);
enum playlist_result
-playlist_delete_id(struct playlist *playlist, unsigned song);
+playlist_delete_id(struct playlist *playlist, struct player_control *pc,
+ unsigned song);
void
-playlist_stop(struct playlist *playlist);
+playlist_stop(struct playlist *playlist, struct player_control *pc);
enum playlist_result
-playlist_play(struct playlist *playlist, int song);
+playlist_play(struct playlist *playlist, struct player_control *pc,
+ int song);
enum playlist_result
-playlist_play_id(struct playlist *playlist, int song);
+playlist_play_id(struct playlist *playlist, struct player_control *pc,
+ int song);
void
-playlist_next(struct playlist *playlist);
+playlist_next(struct playlist *playlist, struct player_control *pc);
void
-playlist_sync(struct playlist *playlist);
+playlist_sync(struct playlist *playlist, struct player_control *pc);
void
-playlist_previous(struct playlist *playlist);
+playlist_previous(struct playlist *playlist, struct player_control *pc);
void
-playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end);
+playlist_shuffle(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end);
void
-playlist_delete_song(struct playlist *playlist, const struct song *song);
+playlist_delete_song(struct playlist *playlist, struct player_control *pc,
+ const struct song *song);
+
+enum playlist_result
+playlist_move_range(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end, int to);
+
+enum playlist_result
+playlist_move_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id, int to);
enum playlist_result
-playlist_move_range(struct playlist *playlist, unsigned start, unsigned end, int to);
+playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
+ unsigned song1, unsigned song2);
enum playlist_result
-playlist_move_id(struct playlist *playlist, unsigned id, int to);
+playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id1, unsigned id2);
enum playlist_result
-playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2);
+playlist_set_priority(struct playlist *playlist, struct player_control *pc,
+ unsigned start_position, unsigned end_position,
+ uint8_t priority);
enum playlist_result
-playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2);
+playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
+ unsigned song_id, uint8_t priority);
bool
playlist_get_repeat(const struct playlist *playlist);
void
-playlist_set_repeat(struct playlist *playlist, bool status);
+playlist_set_repeat(struct playlist *playlist, struct player_control *pc,
+ bool status);
bool
playlist_get_random(const struct playlist *playlist);
void
-playlist_set_random(struct playlist *playlist, bool status);
+playlist_set_random(struct playlist *playlist, struct player_control *pc,
+ bool status);
bool
playlist_get_single(const struct playlist *playlist);
void
-playlist_set_single(struct playlist *playlist, bool status);
+playlist_set_single(struct playlist *playlist, struct player_control *pc,
+ bool status);
bool
playlist_get_consume(const struct playlist *playlist);
@@ -222,10 +232,11 @@ unsigned long
playlist_get_version(const struct playlist *playlist);
enum playlist_result
-playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time);
+playlist_seek_song(struct playlist *playlist, struct player_control *pc,
+ unsigned song, float seek_time);
enum playlist_result
-playlist_seek_song_id(struct playlist *playlist,
+playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
unsigned id, float seek_time);
void
diff --git a/src/playlist/asx_playlist_plugin.c b/src/playlist/asx_playlist_plugin.c
index 39513e710..b711f83f3 100644
--- a/src/playlist/asx_playlist_plugin.c
+++ b/src/playlist/asx_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/asx_playlist_plugin.h b/src/playlist/asx_playlist_plugin.h
index 7ce91aa41..6c01c1209 100644
--- a/src/playlist/asx_playlist_plugin.h
+++ b/src/playlist/asx_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/cue_playlist_plugin.c b/src/playlist/cue_playlist_plugin.c
index b22712bc7..e3619a284 100644
--- a/src/playlist/cue_playlist_plugin.c
+++ b/src/playlist/cue_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/cue_playlist_plugin.h b/src/playlist/cue_playlist_plugin.h
index c89ec55c5..c02e2235a 100644
--- a/src/playlist/cue_playlist_plugin.h
+++ b/src/playlist/cue_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/despotify_playlist_plugin.c b/src/playlist/despotify_playlist_plugin.c
new file mode 100644
index 000000000..39448e01f
--- /dev/null
+++ b/src/playlist/despotify_playlist_plugin.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 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.
+ */
+#include "config.h"
+#include "playlist/despotify_playlist_plugin.h"
+#include "playlist_plugin.h"
+#include "playlist_list.h"
+#include "conf.h"
+#include "uri.h"
+#include "tag.h"
+#include "song.h"
+#include "input_stream.h"
+#include "glib_compat.h"
+#include "despotify_utils.h"
+
+#include <glib.h>
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <despotify.h>
+
+struct despotify_playlist {
+ struct playlist_provider base;
+
+ struct despotify_session *session;
+ GSList *list;
+};
+
+static void
+add_song(struct despotify_playlist *ctx, struct ds_track *track)
+{
+ const char *dsp_scheme = despotify_playlist_plugin.schemes[0];
+ struct song *song;
+ char uri[128];
+ char *ds_uri;
+
+ /* Create a spt://... URI for MPD */
+ g_snprintf(uri, sizeof(uri), "%s://", dsp_scheme);
+ ds_uri = uri + strlen(dsp_scheme) + 3;
+
+ if (despotify_track_to_uri(track, ds_uri) != ds_uri) {
+ /* Should never really fail, but let's be sure */
+ g_debug("Can't add track %s\n", track->title);
+ return;
+ }
+
+ song = song_remote_new(uri);
+ song->tag = mpd_despotify_tag_from_track(track);
+
+ ctx->list = g_slist_prepend(ctx->list, song);
+}
+
+static bool
+parse_track(struct despotify_playlist *ctx,
+ struct ds_link *link)
+{
+ struct ds_track *track;
+
+ track = despotify_link_get_track(ctx->session, link);
+ if (!track)
+ return false;
+ add_song(ctx, track);
+
+ return true;
+}
+
+static bool
+parse_playlist(struct despotify_playlist *ctx,
+ struct ds_link *link)
+{
+ struct ds_playlist *playlist;
+ struct ds_track *track;
+
+ playlist = despotify_link_get_playlist(ctx->session, link);
+ if (!playlist)
+ return false;
+
+ for (track = playlist->tracks; track; track = track->next)
+ add_song(ctx, track);
+
+ return true;
+}
+
+static bool
+despotify_playlist_init(G_GNUC_UNUSED const struct config_param *param)
+{
+ return true;
+}
+
+static void
+despotify_playlist_finish(void)
+{
+}
+
+
+static struct playlist_provider *
+despotify_playlist_open_uri(const char *url)
+{
+ struct despotify_playlist *ctx;
+ struct despotify_session *session;
+ struct ds_link *link;
+ bool parse_result;
+
+ session = mpd_despotify_get_session();
+ if (!session)
+ goto clean_none;
+
+ /* Get link without spt:// */
+ link = despotify_link_from_uri(url + strlen(despotify_playlist_plugin.schemes[0]) + 3);
+ if (!link) {
+ g_debug("Can't find %s\n", url);
+ goto clean_none;
+ }
+
+ ctx = g_new(struct despotify_playlist, 1);
+
+ ctx->list = NULL;
+ ctx->session = session;
+ playlist_provider_init(&ctx->base, &despotify_playlist_plugin);
+
+ switch (link->type)
+ {
+ case LINK_TYPE_TRACK:
+ parse_result = parse_track(ctx, link);
+ break;
+ case LINK_TYPE_PLAYLIST:
+ parse_result = parse_playlist(ctx, link);
+ break;
+ default:
+ parse_result = false;
+ break;
+ }
+ despotify_free_link(link);
+ if (!parse_result)
+ goto clean_playlist;
+
+ ctx->list = g_slist_reverse(ctx->list);
+
+ return &ctx->base;
+
+clean_playlist:
+ g_slist_free(ctx->list);
+clean_none:
+
+ return NULL;
+}
+
+static void
+track_free_callback(gpointer data, G_GNUC_UNUSED gpointer user_data)
+{
+ struct song *song = (struct song *)data;
+
+ song_free(song);
+}
+
+static void
+despotify_playlist_close(struct playlist_provider *_playlist)
+{
+ struct despotify_playlist *ctx = (struct despotify_playlist *)_playlist;
+
+ g_slist_foreach(ctx->list, track_free_callback, NULL);
+ g_slist_free(ctx->list);
+
+ g_free(ctx);
+}
+
+
+static struct song *
+despotify_playlist_read(struct playlist_provider *_playlist)
+{
+ struct despotify_playlist *ctx = (struct despotify_playlist *)_playlist;
+ struct song *out;
+
+ if (!ctx->list)
+ return NULL;
+
+ /* Remove the current track */
+ out = ctx->list->data;
+ ctx->list = g_slist_remove(ctx->list, out);
+
+ return out;
+}
+
+
+static const char *const despotify_schemes[] = {
+ "spt",
+ NULL
+};
+
+const struct playlist_plugin despotify_playlist_plugin = {
+ .name = "despotify",
+
+ .init = despotify_playlist_init,
+ .finish = despotify_playlist_finish,
+ .open_uri = despotify_playlist_open_uri,
+ .read = despotify_playlist_read,
+ .close = despotify_playlist_close,
+
+ .schemes = despotify_schemes,
+};
diff --git a/src/playlist/despotify_playlist_plugin.h b/src/playlist/despotify_playlist_plugin.h
new file mode 100644
index 000000000..f8ee20de0
--- /dev/null
+++ b/src/playlist/despotify_playlist_plugin.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 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_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_H
+#define MPD_PLAYLIST_DESPOTIFY_PLAYLIST_PLUGIN_H
+
+extern const struct playlist_plugin despotify_playlist_plugin;
+
+#endif
diff --git a/src/playlist/extm3u_playlist_plugin.c b/src/playlist/extm3u_playlist_plugin.c
index 9a04aa066..19be8d1c4 100644
--- a/src/playlist/extm3u_playlist_plugin.c
+++ b/src/playlist/extm3u_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include "uri.h"
#include "song.h"
#include "tag.h"
+#include "string_util.h"
#include <glib.h>
@@ -89,7 +90,7 @@ extm3u_parse_tag(const char *line)
/* 0 means unknown duration */
duration = 0;
- name = g_strchug(endptr + 1);
+ name = strchug_fast_c(endptr + 1);
if (*name == 0 && duration == 0)
/* no information available; don't allocate a tag
object */
diff --git a/src/playlist/extm3u_playlist_plugin.h b/src/playlist/extm3u_playlist_plugin.h
index fa726c5f6..5f611ac9c 100644
--- a/src/playlist/extm3u_playlist_plugin.h
+++ b/src/playlist/extm3u_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/flac_playlist_plugin.c b/src/playlist/flac_playlist_plugin.c
index 9d66fb331..8adf694ed 100644
--- a/src/playlist/flac_playlist_plugin.c
+++ b/src/playlist/flac_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/flac_playlist_plugin.h b/src/playlist/flac_playlist_plugin.h
index 7b141264f..231d90e4a 100644
--- a/src/playlist/flac_playlist_plugin.h
+++ b/src/playlist/flac_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/lastfm_playlist_plugin.c b/src/playlist/lastfm_playlist_plugin.c
index afb3979d9..5c803cd99 100644
--- a/src/playlist/lastfm_playlist_plugin.c
+++ b/src/playlist/lastfm_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -139,7 +139,7 @@ lastfm_get(const char *url)
* Ini-style value fetcher.
* @param response data through which to search.
* @param name name of value to search for.
- * @return value for param name in param reponse or NULL on error. Free with g_free.
+ * @return value for param name in param response or NULL on error. Free with g_free.
*/
static char *
lastfm_find(const char *response, const char *name)
diff --git a/src/playlist/lastfm_playlist_plugin.h b/src/playlist/lastfm_playlist_plugin.h
index 363377c21..46a8b0caf 100644
--- a/src/playlist/lastfm_playlist_plugin.h
+++ b/src/playlist/lastfm_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/m3u_playlist_plugin.c b/src/playlist/m3u_playlist_plugin.c
index 221c27277..45b70d2b1 100644
--- a/src/playlist/m3u_playlist_plugin.c
+++ b/src/playlist/m3u_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/m3u_playlist_plugin.h b/src/playlist/m3u_playlist_plugin.h
index 98dcc4729..3890a5fc2 100644
--- a/src/playlist/m3u_playlist_plugin.h
+++ b/src/playlist/m3u_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/pls_playlist_plugin.c b/src/playlist/pls_playlist_plugin.c
index 2a36f12f5..937f02791 100644
--- a/src/playlist/pls_playlist_plugin.c
+++ b/src/playlist/pls_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/pls_playlist_plugin.h b/src/playlist/pls_playlist_plugin.h
index c3bcf3f05..d03435f6d 100644
--- a/src/playlist/pls_playlist_plugin.h
+++ b/src/playlist/pls_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/rss_playlist_plugin.c b/src/playlist/rss_playlist_plugin.c
index b5787bb68..9ce3c6abe 100644
--- a/src/playlist/rss_playlist_plugin.c
+++ b/src/playlist/rss_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/rss_playlist_plugin.h b/src/playlist/rss_playlist_plugin.h
index d8992f2e5..3b376de79 100644
--- a/src/playlist/rss_playlist_plugin.h
+++ b/src/playlist/rss_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/xspf_playlist_plugin.c b/src/playlist/xspf_playlist_plugin.c
index 50f6bd1e7..89a4a08a4 100644
--- a/src/playlist/xspf_playlist_plugin.c
+++ b/src/playlist/xspf_playlist_plugin.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist/xspf_playlist_plugin.h b/src/playlist/xspf_playlist_plugin.h
index ea832207d..4636d7e83 100644
--- a/src/playlist/xspf_playlist_plugin.h
+++ b/src/playlist/xspf_playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_any.c b/src/playlist_any.c
index 39e21b178..bed12206a 100644
--- a/src/playlist_any.c
+++ b/src/playlist_any.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_any.h b/src/playlist_any.h
index 6fed97d15..7c13df718 100644
--- a/src/playlist_any.h
+++ b/src/playlist_any.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_control.c b/src/playlist_control.c
index 76066d274..998294845 100644
--- a/src/playlist_control.c
+++ b/src/playlist_control.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -32,7 +32,8 @@
#undef G_LOG_DOMAIN
#define G_LOG_DOMAIN "playlist"
-void playlist_stop(struct playlist *playlist)
+void
+playlist_stop(struct playlist *playlist, struct player_control *pc)
{
if (!playlist->playing)
return;
@@ -40,7 +41,7 @@ void playlist_stop(struct playlist *playlist)
assert(playlist->current >= 0);
g_debug("stop");
- pc_stop();
+ pc_stop(pc);
playlist->queued = -1;
playlist->playing = false;
@@ -62,11 +63,13 @@ void playlist_stop(struct playlist *playlist)
}
}
-enum playlist_result playlist_play(struct playlist *playlist, int song)
+enum playlist_result
+playlist_play(struct playlist *playlist, struct player_control *pc,
+ int song)
{
unsigned i = song;
- pc_clear_error();
+ pc_clear_error(pc);
if (song == -1) {
/* play any song ("current" song, or the first song */
@@ -77,7 +80,7 @@ enum playlist_result playlist_play(struct playlist *playlist, int song)
if (playlist->playing) {
/* already playing: unpause playback, just in
case it was paused, and return */
- pc_set_pause(false);
+ pc_set_pause(pc, false);
return PLAYLIST_RESULT_SUCCESS;
}
@@ -109,28 +112,29 @@ enum playlist_result playlist_play(struct playlist *playlist, int song)
playlist->stop_on_error = false;
playlist->error_count = 0;
- playlist_play_order(playlist, i);
+ playlist_play_order(playlist, pc, i);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_play_id(struct playlist *playlist, int id)
+playlist_play_id(struct playlist *playlist, struct player_control *pc,
+ int id)
{
int song;
if (id == -1) {
- return playlist_play(playlist, id);
+ return playlist_play(playlist, pc, id);
}
song = queue_id_to_position(&playlist->queue, id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_play(playlist, song);
+ return playlist_play(playlist, pc, song);
}
void
-playlist_next(struct playlist *playlist)
+playlist_next(struct playlist *playlist, struct player_control *pc)
{
int next_order;
int current;
@@ -149,7 +153,7 @@ playlist_next(struct playlist *playlist)
next_order = queue_next_order(&playlist->queue, playlist->current);
if (next_order < 0) {
/* no song after this one: stop playback */
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
/* reset "current song" */
playlist->current = -1;
@@ -170,15 +174,18 @@ playlist_next(struct playlist *playlist)
discard them anyway */
}
- playlist_play_order(playlist, next_order);
+ playlist_play_order(playlist, pc, next_order);
}
/* Consume mode removes each played songs. */
if(playlist->queue.consume)
- playlist_delete(playlist, queue_order_to_position(&playlist->queue, current));
+ playlist_delete(playlist, pc,
+ queue_order_to_position(&playlist->queue,
+ current));
}
-void playlist_previous(struct playlist *playlist)
+void
+playlist_previous(struct playlist *playlist, struct player_control *pc)
{
if (!playlist->playing)
return;
@@ -187,21 +194,22 @@ void playlist_previous(struct playlist *playlist)
if (playlist->current > 0) {
/* play the preceding song */
- playlist_play_order(playlist,
+ playlist_play_order(playlist, pc,
playlist->current - 1);
} else if (playlist->queue.repeat) {
/* play the last song in "repeat" mode */
- playlist_play_order(playlist,
+ playlist_play_order(playlist, pc,
queue_length(&playlist->queue) - 1);
} else {
/* re-start playing the current song if it's
the first one */
- playlist_play_order(playlist, playlist->current);
+ playlist_play_order(playlist, pc, playlist->current);
}
}
enum playlist_result
-playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
+playlist_seek_song(struct playlist *playlist, struct player_control *pc,
+ unsigned song, float seek_time)
{
const struct song *queued;
unsigned i;
@@ -217,7 +225,7 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
else
i = song;
- pc_clear_error();
+ pc_clear_error(pc);
playlist->stop_on_error = true;
playlist->error_count = 0;
@@ -231,25 +239,26 @@ playlist_seek_song(struct playlist *playlist, unsigned song, float seek_time)
queued = NULL;
}
- success = pc_seek(queue_get_order(&playlist->queue, i), seek_time);
+ success = pc_seek(pc, queue_get_order(&playlist->queue, i), seek_time);
if (!success) {
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_NOT_PLAYING;
}
playlist->queued = -1;
- playlist_update_queued_song(playlist, NULL);
+ playlist_update_queued_song(playlist, pc, NULL);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_seek_song_id(struct playlist *playlist, unsigned id, float seek_time)
+playlist_seek_song_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id, float seek_time)
{
int song = queue_id_to_position(&playlist->queue, id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_seek_song(playlist, song, seek_time);
+ return playlist_seek_song(playlist, pc, song, seek_time);
}
diff --git a/src/playlist_database.c b/src/playlist_database.c
index 0a8a6f139..2ad913d00 100644
--- a/src/playlist_database.c
+++ b/src/playlist_database.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,7 @@
#include "playlist_database.h"
#include "playlist_vector.h"
#include "text_file.h"
+#include "string_util.h"
#include <string.h>
#include <stdlib.h>
@@ -62,7 +63,7 @@ playlist_metadata_load(FILE *fp, struct playlist_vector *pv, const char *name,
}
*colon++ = 0;
- value = g_strchug(colon);
+ value = strchug_fast_c(colon);
if (strcmp(line, "mtime") == 0)
pm.mtime = strtol(value, NULL, 10);
diff --git a/src/playlist_database.h b/src/playlist_database.h
index 7e114abdd..f80ebdaff 100644
--- a/src/playlist_database.h
+++ b/src/playlist_database.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_edit.c b/src/playlist_edit.c
index c54b72750..92c3d44b0 100644
--- a/src/playlist_edit.c
+++ b/src/playlist_edit.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -43,16 +43,17 @@ static void playlist_increment_version(struct playlist *playlist)
idle_add(IDLE_PLAYLIST);
}
-void playlist_clear(struct playlist *playlist)
+void
+playlist_clear(struct playlist *playlist, struct player_control *pc)
{
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
/* make sure there are no references to allocated songs
anymore */
for (unsigned i = 0; i < queue_length(&playlist->queue); i++) {
const struct song *song = queue_get(&playlist->queue, i);
if (!song_in_database(song))
- pc_song_deleted(song);
+ pc_song_deleted(pc, song);
}
queue_clear(&playlist->queue);
@@ -64,8 +65,8 @@ void playlist_clear(struct playlist *playlist)
#ifndef WIN32
enum playlist_result
-playlist_append_file(struct playlist *playlist, const char *path, int uid,
- unsigned *added_id)
+playlist_append_file(struct playlist *playlist, struct player_control *pc,
+ const char *path, int uid, unsigned *added_id)
{
int ret;
struct stat st;
@@ -87,12 +88,12 @@ playlist_append_file(struct playlist *playlist, const char *path, int uid,
if (song == NULL)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_append_song(playlist, song, added_id);
+ return playlist_append_song(playlist, pc, song, added_id);
}
#endif
enum playlist_result
-playlist_append_song(struct playlist *playlist,
+playlist_append_song(struct playlist *playlist, struct player_control *pc,
struct song *song, unsigned *added_id)
{
const struct song *queued;
@@ -121,7 +122,7 @@ playlist_append_song(struct playlist *playlist,
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
if (added_id)
*added_id = id;
@@ -145,8 +146,8 @@ song_by_uri(const char *uri)
}
enum playlist_result
-playlist_append_uri(struct playlist *playlist, const char *uri,
- unsigned *added_id)
+playlist_append_uri(struct playlist *playlist, struct player_control *pc,
+ const char *uri, unsigned *added_id)
{
struct song *song;
@@ -156,11 +157,12 @@ playlist_append_uri(struct playlist *playlist, const char *uri,
if (song == NULL)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_append_song(playlist, song, added_id);
+ return playlist_append_song(playlist, pc, song, added_id);
}
enum playlist_result
-playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2)
+playlist_swap_songs(struct playlist *playlist, struct player_control *pc,
+ unsigned song1, unsigned song2)
{
const struct song *queued;
@@ -192,13 +194,14 @@ playlist_swap_songs(struct playlist *playlist, unsigned song1, unsigned song2)
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2)
+playlist_swap_songs_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id1, unsigned id2)
{
int song1 = queue_id_to_position(&playlist->queue, id1);
int song2 = queue_id_to_position(&playlist->queue, id2);
@@ -206,12 +209,67 @@ playlist_swap_songs_id(struct playlist *playlist, unsigned id1, unsigned id2)
if (song1 < 0 || song2 < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_swap_songs(playlist, song1, song2);
+ return playlist_swap_songs(playlist, pc, song1, song2);
+}
+
+enum playlist_result
+playlist_set_priority(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end,
+ uint8_t priority)
+{
+ if (start >= queue_length(&playlist->queue))
+ return PLAYLIST_RESULT_BAD_RANGE;
+
+ if (end > queue_length(&playlist->queue))
+ end = queue_length(&playlist->queue);
+
+ if (start >= end)
+ return PLAYLIST_RESULT_SUCCESS;
+
+ /* remember "current" and "queued" */
+
+ int current_position = playlist->current >= 0
+ ? (int)queue_order_to_position(&playlist->queue,
+ playlist->current)
+ : -1;
+
+ const struct song *queued = playlist_get_queued_song(playlist);
+
+ /* apply the priority changes */
+
+ queue_set_priority_range(&playlist->queue, start, end, priority,
+ playlist->current);
+
+ playlist_increment_version(playlist);
+
+ /* restore "current" and choose a new "queued" */
+
+ if (current_position >= 0)
+ playlist->current = queue_position_to_order(&playlist->queue,
+ current_position);
+
+ playlist_update_queued_song(playlist, pc, queued);
+
+ return PLAYLIST_RESULT_SUCCESS;
+}
+
+enum playlist_result
+playlist_set_priority_id(struct playlist *playlist, struct player_control *pc,
+ unsigned song_id, uint8_t priority)
+{
+ int song_position = queue_id_to_position(&playlist->queue, song_id);
+ if (song_position < 0)
+ return PLAYLIST_RESULT_NO_SUCH_SONG;
+
+ return playlist_set_priority(playlist, pc,
+ song_position, song_position + 1,
+ priority);
+
}
static void
-playlist_delete_internal(struct playlist *playlist, unsigned song,
- const struct song **queued_p)
+playlist_delete_internal(struct playlist *playlist, struct player_control *pc,
+ unsigned song, const struct song **queued_p)
{
unsigned songOrder;
@@ -220,11 +278,11 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
songOrder = queue_position_to_order(&playlist->queue, song);
if (playlist->playing && playlist->current == (int)songOrder) {
- bool paused = pc_get_state() == PLAYER_STATE_PAUSE;
+ bool paused = pc_get_state(pc) == PLAYER_STATE_PAUSE;
/* the current song is going to be deleted: stop the player */
- pc_stop();
+ pc_stop(pc);
playlist->playing = false;
/* see which song is going to be played instead */
@@ -236,11 +294,11 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
if (playlist->current >= 0 && !paused)
/* play the song after the deleted one */
- playlist_play_order(playlist, playlist->current);
+ playlist_play_order(playlist, pc, playlist->current);
else
/* no songs left to play, stop playback
completely */
- playlist_stop(playlist);
+ playlist_stop(playlist, pc);
*queued_p = NULL;
} else if (playlist->current == (int)songOrder)
@@ -251,7 +309,7 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
/* now do it: remove the song */
if (!song_in_database(queue_get(&playlist->queue, song)))
- pc_song_deleted(queue_get(&playlist->queue, song));
+ pc_song_deleted(pc, queue_get(&playlist->queue, song));
queue_delete(&playlist->queue, song);
@@ -263,7 +321,8 @@ playlist_delete_internal(struct playlist *playlist, unsigned song,
}
enum playlist_result
-playlist_delete(struct playlist *playlist, unsigned song)
+playlist_delete(struct playlist *playlist, struct player_control *pc,
+ unsigned song)
{
const struct song *queued;
@@ -272,16 +331,17 @@ playlist_delete(struct playlist *playlist, unsigned song)
queued = playlist_get_queued_song(playlist);
- playlist_delete_internal(playlist, song, &queued);
+ playlist_delete_internal(playlist, pc, song, &queued);
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end)
+playlist_delete_range(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end)
{
const struct song *queued;
@@ -297,37 +357,39 @@ playlist_delete_range(struct playlist *playlist, unsigned start, unsigned end)
queued = playlist_get_queued_song(playlist);
do {
- playlist_delete_internal(playlist, --end, &queued);
+ playlist_delete_internal(playlist, pc, --end, &queued);
} while (end != start);
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_delete_id(struct playlist *playlist, unsigned id)
+playlist_delete_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id)
{
int song = queue_id_to_position(&playlist->queue, id);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_delete(playlist, song);
+ return playlist_delete(playlist, pc, song);
}
void
-playlist_delete_song(struct playlist *playlist, const struct song *song)
+playlist_delete_song(struct playlist *playlist, struct player_control *pc,
+ const struct song *song)
{
for (int i = queue_length(&playlist->queue) - 1; i >= 0; --i)
if (song == queue_get(&playlist->queue, i))
- playlist_delete(playlist, i);
+ playlist_delete(playlist, pc, i);
- pc_song_deleted(song);
+ pc_song_deleted(pc, song);
}
enum playlist_result
-playlist_move_range(struct playlist *playlist,
+playlist_move_range(struct playlist *playlist, struct player_control *pc,
unsigned start, unsigned end, int to)
{
const struct song *queued;
@@ -382,23 +444,25 @@ playlist_move_range(struct playlist *playlist,
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
return PLAYLIST_RESULT_SUCCESS;
}
enum playlist_result
-playlist_move_id(struct playlist *playlist, unsigned id1, int to)
+playlist_move_id(struct playlist *playlist, struct player_control *pc,
+ unsigned id1, int to)
{
int song = queue_id_to_position(&playlist->queue, id1);
if (song < 0)
return PLAYLIST_RESULT_NO_SUCH_SONG;
- return playlist_move_range(playlist, song, song+1, to);
+ return playlist_move_range(playlist, pc, song, song+1, to);
}
void
-playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end)
+playlist_shuffle(struct playlist *playlist, struct player_control *pc,
+ unsigned start, unsigned end)
{
const struct song *queued;
@@ -440,5 +504,5 @@ playlist_shuffle(struct playlist *playlist, unsigned start, unsigned end)
playlist_increment_version(playlist);
- playlist_update_queued_song(playlist, queued);
+ playlist_update_queued_song(playlist, pc, queued);
}
diff --git a/src/playlist_error.h b/src/playlist_error.h
new file mode 100644
index 000000000..ad9c62cf1
--- /dev/null
+++ b/src/playlist_error.h
@@ -0,0 +1,49 @@
+/*
+ * 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_PLAYLIST_ERROR_H
+#define MPD_PLAYLIST_ERROR_H
+
+#include <glib.h>
+
+enum playlist_result {
+ PLAYLIST_RESULT_SUCCESS,
+ PLAYLIST_RESULT_ERRNO,
+ PLAYLIST_RESULT_DENIED,
+ PLAYLIST_RESULT_NO_SUCH_SONG,
+ PLAYLIST_RESULT_NO_SUCH_LIST,
+ PLAYLIST_RESULT_LIST_EXISTS,
+ PLAYLIST_RESULT_BAD_NAME,
+ PLAYLIST_RESULT_BAD_RANGE,
+ PLAYLIST_RESULT_NOT_PLAYING,
+ PLAYLIST_RESULT_TOO_LARGE,
+ PLAYLIST_RESULT_DISABLED,
+};
+
+/**
+ * Quark for GError.domain; the code is an enum #playlist_result.
+ */
+G_GNUC_CONST
+static inline GQuark
+playlist_quark(void)
+{
+ return g_quark_from_static_string("playlist");
+}
+
+#endif
diff --git a/src/playlist_global.c b/src/playlist_global.c
index 2833b62ed..650b88bb8 100644
--- a/src/playlist_global.c
+++ b/src/playlist_global.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -26,6 +26,7 @@
#include "playlist.h"
#include "playlist_state.h"
#include "event_pipe.h"
+#include "main.h"
struct playlist g_playlist;
@@ -38,7 +39,7 @@ playlist_tag_event(void)
static void
playlist_event(void)
{
- playlist_sync(&g_playlist);
+ playlist_sync(&g_playlist, global_player_control);
}
void
diff --git a/src/playlist_internal.h b/src/playlist_internal.h
index 9d205188f..81b175176 100644
--- a/src/playlist_internal.h
+++ b/src/playlist_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -27,6 +27,8 @@
#include "playlist.h"
+struct player_control;
+
/**
* Returns the song object which is currently queued. Returns none if
* there is none (yet?) or if MPD isn't playing.
@@ -44,9 +46,11 @@ playlist_get_queued_song(struct playlist *playlist);
*/
void
playlist_update_queued_song(struct playlist *playlist,
+ struct player_control *pc,
const struct song *prev);
void
-playlist_play_order(struct playlist *playlist, int orderNum);
+playlist_play_order(struct playlist *playlist, struct player_control *pc,
+ int orderNum);
#endif
diff --git a/src/playlist_list.c b/src/playlist_list.c
index 019654bfc..04c3fa292 100644
--- a/src/playlist_list.c
+++ b/src/playlist_list.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include "playlist/m3u_playlist_plugin.h"
#include "playlist/xspf_playlist_plugin.h"
#include "playlist/lastfm_playlist_plugin.h"
+#include "playlist/despotify_playlist_plugin.h"
#include "playlist/pls_playlist_plugin.h"
#include "playlist/asx_playlist_plugin.h"
#include "playlist/rss_playlist_plugin.h"
@@ -31,7 +32,7 @@
#include "playlist/flac_playlist_plugin.h"
#include "input_stream.h"
#include "uri.h"
-#include "utils.h"
+#include "string_util.h"
#include "conf.h"
#include "glib_compat.h"
#include "mpd_error.h"
@@ -47,6 +48,9 @@ static const struct playlist_plugin *const playlist_plugins[] = {
&pls_playlist_plugin,
&asx_playlist_plugin,
&rss_playlist_plugin,
+#ifdef ENABLE_DESPOTIFY
+ &despotify_playlist_plugin,
+#endif
#ifdef ENABLE_LASTFM
&lastfm_playlist_plugin,
#endif
diff --git a/src/playlist_list.h b/src/playlist_list.h
index 3710589a2..69eb26b17 100644
--- a/src/playlist_list.h
+++ b/src/playlist_list.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_mapper.c b/src/playlist_mapper.c
index 99b322073..824b40e0e 100644
--- a/src/playlist_mapper.c
+++ b/src/playlist_mapper.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_mapper.h b/src/playlist_mapper.h
index b98af1b13..ab8ba982c 100644
--- a/src/playlist_mapper.h
+++ b/src/playlist_mapper.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_plugin.h b/src/playlist_plugin.h
index 3d840573e..d7d7c7769 100644
--- a/src/playlist_plugin.h
+++ b/src/playlist_plugin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_print.c b/src/playlist_print.c
index 89ab2e5ab..9962ffc35 100644
--- a/src/playlist_print.c
+++ b/src/playlist_print.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -23,6 +23,7 @@
#include "playlist_plugin.h"
#include "playlist_any.h"
#include "playlist_song.h"
+#include "playlist.h"
#include "queue_print.h"
#include "stored_playlist.h"
#include "song_print.h"
@@ -116,11 +117,12 @@ playlist_print_changes_position(struct client *client,
}
bool
-spl_print(struct client *client, const char *name_utf8, bool detail)
+spl_print(struct client *client, const char *name_utf8, bool detail,
+ GError **error_r)
{
GPtrArray *list;
- list = spl_load(name_utf8);
+ list = spl_load(name_utf8, error_r);
if (list == NULL)
return false;
@@ -153,7 +155,7 @@ playlist_provider_print(struct client *client, const char *uri,
char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;
while ((song = playlist_plugin_read(playlist)) != NULL) {
- song = playlist_check_translate_song(song, base_uri);
+ song = playlist_check_translate_song(song, base_uri, false);
if (song == NULL)
continue;
diff --git a/src/playlist_print.h b/src/playlist_print.h
index b3a0446ed..d4f1911d2 100644
--- a/src/playlist_print.h
+++ b/src/playlist_print.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
#ifndef PLAYLIST_PRINT_H
#define PLAYLIST_PRINT_H
+#include <glib.h>
#include <stdbool.h>
#include <stdint.h>
@@ -99,7 +100,8 @@ playlist_print_changes_position(struct client *client,
* @return true on success, false if the playlist does not exist
*/
bool
-spl_print(struct client *client, const char *name_utf8, bool detail);
+spl_print(struct client *client, const char *name_utf8, bool detail,
+ GError **error_r);
/**
* Send the playlist file to the client.
diff --git a/src/playlist_queue.c b/src/playlist_queue.c
index 635e23a28..d368fcb23 100644
--- a/src/playlist_queue.c
+++ b/src/playlist_queue.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -22,23 +22,25 @@
#include "playlist_plugin.h"
#include "playlist_any.h"
#include "playlist_song.h"
+#include "playlist.h"
#include "song.h"
#include "input_stream.h"
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
- struct playlist *dest)
+ struct playlist *dest, struct player_control *pc,
+ bool secure)
{
enum playlist_result result;
struct song *song;
char *base_uri = uri != NULL ? g_path_get_dirname(uri) : NULL;
while ((song = playlist_plugin_read(source)) != NULL) {
- song = playlist_check_translate_song(song, base_uri);
+ song = playlist_check_translate_song(song, base_uri, secure);
if (song == NULL)
continue;
- result = playlist_append_song(dest, song, NULL);
+ result = playlist_append_song(dest, pc, song, NULL);
if (result != PLAYLIST_RESULT_SUCCESS) {
if (!song_in_database(song))
song_free(song);
@@ -53,7 +55,9 @@ playlist_load_into_queue(const char *uri, struct playlist_provider *source,
}
enum playlist_result
-playlist_open_into_queue(const char *uri, struct playlist *dest)
+playlist_open_into_queue(const char *uri,
+ struct playlist *dest, struct player_control *pc,
+ bool secure)
{
struct input_stream *is;
struct playlist_provider *playlist = playlist_open_any(uri, &is);
@@ -61,7 +65,7 @@ playlist_open_into_queue(const char *uri, struct playlist *dest)
return PLAYLIST_RESULT_NO_SUCH_LIST;
enum playlist_result result =
- playlist_load_into_queue(uri, playlist, dest);
+ playlist_load_into_queue(uri, playlist, dest, pc, secure);
playlist_plugin_close(playlist);
if (is != NULL)
diff --git a/src/playlist_queue.h b/src/playlist_queue.h
index 530d4b4be..3ae63bc16 100644
--- a/src/playlist_queue.h
+++ b/src/playlist_queue.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,10 +24,13 @@
#ifndef MPD_PLAYLIST_QUEUE_H
#define MPD_PLAYLIST_QUEUE_H
-#include "playlist.h"
+#include "playlist_error.h"
+
+#include <stdbool.h>
struct playlist_provider;
struct playlist;
+struct player_control;
/**
* Loads the contents of a playlist and append it to the specified
@@ -38,14 +41,17 @@ struct playlist;
*/
enum playlist_result
playlist_load_into_queue(const char *uri, struct playlist_provider *source,
- struct playlist *dest);
+ struct playlist *dest, struct player_control *pc,
+ bool secure);
/**
* Opens a playlist with a playlist plugin and append to the specified
* play queue.
*/
enum playlist_result
-playlist_open_into_queue(const char *uri, struct playlist *dest);
+playlist_open_into_queue(const char *uri,
+ struct playlist *dest, struct player_control *pc,
+ bool secure);
#endif
diff --git a/src/playlist_save.c b/src/playlist_save.c
index 8ddc93ec9..b8e03ea85 100644
--- a/src/playlist_save.c
+++ b/src/playlist_save.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,7 +19,9 @@
#include "config.h"
#include "playlist_save.h"
+#include "playlist.h"
#include "stored_playlist.h"
+#include "queue.h"
#include "song.h"
#include "mapper.h"
#include "path.h"
@@ -108,18 +110,19 @@ spl_save_playlist(const char *name_utf8, const struct playlist *playlist)
return spl_save_queue(name_utf8, &playlist->queue);
}
-enum playlist_result
-playlist_load_spl(struct playlist *playlist, const char *name_utf8)
+bool
+playlist_load_spl(struct playlist *playlist, struct player_control *pc,
+ const char *name_utf8, GError **error_r)
{
GPtrArray *list;
- list = spl_load(name_utf8);
+ list = spl_load(name_utf8, error_r);
if (list == NULL)
- return PLAYLIST_RESULT_NO_SUCH_LIST;
+ return false;
for (unsigned i = 0; i < list->len; ++i) {
const char *temp = g_ptr_array_index(list, i);
- if ((playlist_append_uri(playlist, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
+ if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
/* for windows compatibility, convert slashes */
char *temp2 = g_strdup(temp);
char *p = temp2;
@@ -128,7 +131,7 @@ playlist_load_spl(struct playlist *playlist, const char *name_utf8)
*p = '/';
p++;
}
- if ((playlist_append_uri(playlist, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
+ if ((playlist_append_uri(playlist, pc, temp, NULL)) != PLAYLIST_RESULT_SUCCESS) {
g_warning("can't add file \"%s\"", temp2);
}
g_free(temp2);
@@ -136,5 +139,5 @@ playlist_load_spl(struct playlist *playlist, const char *name_utf8)
}
spl_free(list);
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
}
diff --git a/src/playlist_save.h b/src/playlist_save.h
index a0131cf7f..f8bfb8355 100644
--- a/src/playlist_save.h
+++ b/src/playlist_save.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,11 +20,15 @@
#ifndef MPD_PLAYLIST_SAVE_H
#define MPD_PLAYLIST_SAVE_H
-#include "playlist.h"
+#include "playlist_error.h"
+#include <stdbool.h>
#include <stdio.h>
struct song;
+struct queue;
+struct playlist;
+struct player_control;
void
playlist_print_song(FILE *fp, const struct song *song);
@@ -48,7 +52,8 @@ spl_save_playlist(const char *name_utf8, const struct playlist *playlist);
* Loads a stored playlist file, and append all songs to the global
* playlist.
*/
-enum playlist_result
-playlist_load_spl(struct playlist *playlist, const char *name_utf8);
+bool
+playlist_load_spl(struct playlist *playlist, struct player_control *pc,
+ const char *name_utf8, GError **error_r);
#endif
diff --git a/src/playlist_song.c b/src/playlist_song.c
index 1a543a0b8..8c966d549 100644
--- a/src/playlist_song.c
+++ b/src/playlist_song.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -84,7 +84,8 @@ apply_song_metadata(struct song *dest, const struct song *src)
}
struct song *
-playlist_check_translate_song(struct song *song, const char *base_uri)
+playlist_check_translate_song(struct song *song, const char *base_uri,
+ bool secure)
{
struct song *dest;
@@ -118,16 +119,17 @@ playlist_check_translate_song(struct song *song, const char *base_uri)
? map_uri_fs(base_uri)
: map_directory_fs(db_get_root());
- if (prefix == NULL || !g_str_has_prefix(uri, prefix) ||
- uri[strlen(prefix)] != '/') {
+ if (prefix != NULL && g_str_has_prefix(uri, prefix) &&
+ uri[strlen(prefix)] == '/')
+ uri += strlen(prefix) + 1;
+ else if (!secure) {
/* local files must be relative to the music
- directory */
+ directory when "secure" is enabled */
g_free(prefix);
song_free(song);
return NULL;
}
- uri += strlen(prefix) + 1;
g_free(prefix);
}
diff --git a/src/playlist_song.h b/src/playlist_song.h
index 5a2e4c2b0..ea8786912 100644
--- a/src/playlist_song.h
+++ b/src/playlist_song.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,12 +20,18 @@
#ifndef MPD_PLAYLIST_SONG_H
#define MPD_PLAYLIST_SONG_H
+#include <stdbool.h>
+
/**
* Verifies the song, returns NULL if it is unsafe. Translate the
* song to a new song object within the database, if it is a local
* file. The old song object is freed.
+ *
+ * @param secure if true, then local files are only allowed if they
+ * are relative to base_uri
*/
struct song *
-playlist_check_translate_song(struct song *song, const char *base_uri);
+playlist_check_translate_song(struct song *song, const char *base_uri,
+ bool secure);
#endif
diff --git a/src/playlist_state.c b/src/playlist_state.c
index bb9897e01..4aa2c2c92 100644
--- a/src/playlist_state.c
+++ b/src/playlist_state.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -29,6 +29,7 @@
#include "queue_save.h"
#include "path.h"
#include "text_file.h"
+#include "conf.h"
#include <string.h>
#include <stdlib.h>
@@ -53,11 +54,12 @@
#define PLAYLIST_BUFFER_SIZE 2*MPD_PATH_MAX
void
-playlist_state_save(FILE *fp, const struct playlist *playlist)
+playlist_state_save(FILE *fp, const struct playlist *playlist,
+ struct player_control *pc)
{
struct player_status player_status;
- pc_get_status(&player_status);
+ pc_get_status(pc, &player_status);
fputs(PLAYLIST_STATE_FILE_STATE, fp);
@@ -89,10 +91,11 @@ playlist_state_save(FILE *fp, const struct playlist *playlist)
fprintf(fp, PLAYLIST_STATE_FILE_CONSUME "%i\n",
playlist->queue.consume);
fprintf(fp, PLAYLIST_STATE_FILE_CROSSFADE "%i\n",
- (int)(pc_get_cross_fade()));
- fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n", pc_get_mixramp_db());
+ (int)(pc_get_cross_fade(pc)));
+ fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDB "%f\n",
+ pc_get_mixramp_db(pc));
fprintf(fp, PLAYLIST_STATE_FILE_MIXRAMPDELAY "%f\n",
- pc_get_mixramp_delay());
+ pc_get_mixramp_delay(pc));
fputs(PLAYLIST_STATE_FILE_PLAYLIST_BEGIN "\n", fp);
queue_save(fp, &playlist->queue);
fputs(PLAYLIST_STATE_FILE_PLAYLIST_END "\n", fp);
@@ -123,11 +126,11 @@ playlist_state_load(FILE *fp, GString *buffer, struct playlist *playlist)
bool
playlist_state_restore(const char *line, FILE *fp, GString *buffer,
- struct playlist *playlist)
+ struct playlist *playlist, struct player_control *pc)
{
int current = -1;
int seek_time = 0;
- int state = PLAYER_STATE_STOP;
+ enum player_state state = PLAYER_STATE_STOP;
bool random_mode = false;
if (!g_str_has_prefix(line, PLAYLIST_STATE_FILE_STATE))
@@ -148,16 +151,16 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_REPEAT)]),
"1") == 0) {
- playlist_set_repeat(playlist, true);
+ playlist_set_repeat(playlist, pc, true);
} else
- playlist_set_repeat(playlist, false);
+ playlist_set_repeat(playlist, pc, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_SINGLE)) {
if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_SINGLE)]),
"1") == 0) {
- playlist_set_single(playlist, true);
+ playlist_set_single(playlist, pc, true);
} else
- playlist_set_single(playlist, false);
+ playlist_set_single(playlist, pc, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CONSUME)) {
if (strcmp
(&(line[strlen(PLAYLIST_STATE_FILE_CONSUME)]),
@@ -166,11 +169,14 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
} else
playlist_set_consume(playlist, false);
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_CROSSFADE)) {
- pc_set_cross_fade(atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
+ pc_set_cross_fade(pc,
+ atoi(line + strlen(PLAYLIST_STATE_FILE_CROSSFADE)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDB)) {
- pc_set_mixramp_db(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB)));
+ pc_set_mixramp_db(pc,
+ atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDB)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_MIXRAMPDELAY)) {
- pc_set_mixramp_delay(atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY)));
+ pc_set_mixramp_delay(pc,
+ atof(line + strlen(PLAYLIST_STATE_FILE_MIXRAMPDELAY)));
} else if (g_str_has_prefix(line, PLAYLIST_STATE_FILE_RANDOM)) {
random_mode =
strcmp(line + strlen(PLAYLIST_STATE_FILE_RANDOM),
@@ -185,38 +191,46 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
}
}
- playlist_set_random(playlist, random_mode);
+ playlist_set_random(playlist, pc, random_mode);
if (!queue_is_empty(&playlist->queue)) {
if (!queue_valid_position(&playlist->queue, current))
current = 0;
+ if (state == PLAYER_STATE_PLAY &&
+ config_get_bool("restore_paused", false))
+ /* the user doesn't want MPD to auto-start
+ playback after startup; fall back to
+ "pause" */
+ state = PLAYER_STATE_PAUSE;
+
/* enable all devices for the first time; this must be
called here, after the audio output states were
restored, before playback begins */
if (state != PLAYER_STATE_STOP)
- pc_update_audio();
+ pc_update_audio(pc);
if (state == PLAYER_STATE_STOP /* && config_option */)
playlist->current = current;
else if (seek_time == 0)
- playlist_play(playlist, current);
+ playlist_play(playlist, pc, current);
else
- playlist_seek_song(playlist, current, seek_time);
+ playlist_seek_song(playlist, pc, current, seek_time);
if (state == PLAYER_STATE_PAUSE)
- pc_pause();
+ pc_pause(pc);
}
return true;
}
unsigned
-playlist_state_get_hash(const struct playlist *playlist)
+playlist_state_get_hash(const struct playlist *playlist,
+ struct player_control *pc)
{
struct player_status player_status;
- pc_get_status(&player_status);
+ pc_get_status(pc, &player_status);
return playlist->queue.version ^
(player_status.state != PLAYER_STATE_STOP
@@ -226,7 +240,7 @@ playlist_state_get_hash(const struct playlist *playlist)
? (queue_order_to_position(&playlist->queue,
playlist->current) << 16)
: 0) ^
- ((int)pc_get_cross_fade() << 20) ^
+ ((int)pc_get_cross_fade(pc) << 20) ^
(player_status.state << 24) ^
(playlist->queue.random << 27) ^
(playlist->queue.repeat << 28) ^
diff --git a/src/playlist_state.h b/src/playlist_state.h
index 8ca3657f2..f67d01d2c 100644
--- a/src/playlist_state.h
+++ b/src/playlist_state.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -30,13 +30,15 @@
#include <stdio.h>
struct playlist;
+struct player_control;
void
-playlist_state_save(FILE *fp, const struct playlist *playlist);
+playlist_state_save(FILE *fp, const struct playlist *playlist,
+ struct player_control *pc);
bool
playlist_state_restore(const char *line, FILE *fp, GString *buffer,
- struct playlist *playlist);
+ struct playlist *playlist, struct player_control *pc);
/**
* Generates a hash number for the current state of the playlist and
@@ -45,6 +47,7 @@ playlist_state_restore(const char *line, FILE *fp, GString *buffer,
* be saved.
*/
unsigned
-playlist_state_get_hash(const struct playlist *playlist);
+playlist_state_get_hash(const struct playlist *playlist,
+ struct player_control *pc);
#endif
diff --git a/src/playlist_vector.c b/src/playlist_vector.c
index 7c1765a98..cfbe8939e 100644
--- a/src/playlist_vector.c
+++ b/src/playlist_vector.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/playlist_vector.h b/src/playlist_vector.h
index 62861ae49..8aa19a4e0 100644
--- a/src/playlist_vector.h
+++ b/src/playlist_vector.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/poison.h b/src/poison.h
index 3654f2e9c..c95b5d005 100644
--- a/src/poison.h
+++ b/src/poison.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/queue.c b/src/queue.c
index dd0b48cb5..cd932875e 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -21,6 +21,8 @@
#include "queue.h"
#include "song.h"
+#include <stdlib.h>
+
/**
* Generate a non-existing id number.
*/
@@ -104,6 +106,7 @@ queue_append(struct queue *queue, struct song *song)
.song = song,
.id = id,
.version = queue->version,
+ .priority = 0,
};
queue->order[queue->length] = queue->length;
@@ -220,6 +223,30 @@ queue_move_range(struct queue *queue, unsigned start, unsigned end, unsigned to)
}
}
+/**
+ * Moves a song to a new position in the "order" list.
+ */
+static void
+queue_move_order(struct queue *queue, unsigned from_order, unsigned to_order)
+{
+ assert(queue != NULL);
+ assert(from_order < queue->length);
+ assert(to_order <= queue->length);
+
+ const unsigned from_position =
+ queue_order_to_position(queue, from_order);
+
+ if (from_order < to_order) {
+ for (unsigned i = from_order; i < to_order; ++i)
+ queue->order[i] = queue->order[i + 1];
+ } else {
+ for (unsigned i = from_order; i > to_order; --i)
+ queue->order[i] = queue->order[i - 1];
+ }
+
+ queue->order[to_order] = from_position;
+}
+
void
queue_delete(struct queue *queue, unsigned position)
{
@@ -308,15 +335,123 @@ queue_finish(struct queue *queue)
g_rand_free(queue->rand);
}
-void
-queue_shuffle_order(struct queue *queue)
+static const struct queue_item *
+queue_get_order_item_const(const struct queue *queue, unsigned order)
+{
+ assert(queue != NULL);
+ assert(order < queue->length);
+
+ return &queue->items[queue->order[order]];
+}
+
+static uint8_t
+queue_get_order_priority(const struct queue *queue, unsigned order)
+{
+ return queue_get_order_item_const(queue, order)->priority;
+}
+
+static gint
+queue_item_compare_order_priority(gconstpointer av, gconstpointer bv,
+ gpointer user_data)
+{
+ const struct queue *queue = user_data;
+ const unsigned *const ap = av;
+ const unsigned *const bp = bv;
+ assert(ap >= queue->order && ap < queue->order + queue->length);
+ assert(bp >= queue->order && bp < queue->order + queue->length);
+ uint8_t a = queue->items[*ap].priority;
+ uint8_t b = queue->items[*bp].priority;
+
+ if (G_LIKELY(a == b))
+ return 0;
+ else if (a > b)
+ return -1;
+ else
+ return 1;
+}
+
+static void
+queue_sort_order_by_priority(struct queue *queue, unsigned start, unsigned end)
{
+ assert(queue != NULL);
assert(queue->random);
+ assert(start <= end);
+ assert(end <= queue->length);
- for (unsigned i = 0; i < queue->length; i++)
+ g_qsort_with_data(&queue->order[start], end - start,
+ sizeof(queue->order[0]),
+ queue_item_compare_order_priority,
+ queue);
+}
+
+/**
+ * Shuffle the order of items in the specified range, ignoring their
+ * priorities.
+ */
+static void
+queue_shuffle_order_range(struct queue *queue, unsigned start, unsigned end)
+{
+ assert(queue != NULL);
+ assert(queue->random);
+ assert(start <= end);
+ assert(end <= queue->length);
+
+ for (unsigned i = start; i < end; ++i)
queue_swap_order(queue, i,
- g_rand_int_range(queue->rand, i,
- queue->length));
+ g_rand_int_range(queue->rand, i, end));
+}
+
+/**
+ * Sort the "order" of items by priority, and then shuffle each
+ * priority group.
+ */
+void
+queue_shuffle_order_range_with_priority(struct queue *queue,
+ unsigned start, unsigned end)
+{
+ assert(queue != NULL);
+ assert(queue->random);
+ assert(start <= end);
+ assert(end <= queue->length);
+
+ if (start == end)
+ return;
+
+ /* first group the range by priority */
+ queue_sort_order_by_priority(queue, start, end);
+
+ /* now shuffle each priority group */
+ unsigned group_start = start;
+ uint8_t group_priority = queue_get_order_priority(queue, start);
+
+ for (unsigned i = start + 1; i < end; ++i) {
+ uint8_t priority = queue_get_order_priority(queue, i);
+ assert(priority <= group_priority);
+
+ if (priority != group_priority) {
+ /* start of a new group - shuffle the one that
+ has just ended */
+ queue_shuffle_order_range(queue, group_start, i);
+ group_start = i;
+ group_priority = priority;
+ }
+ }
+
+ /* shuffle the last group */
+ queue_shuffle_order_range(queue, group_start, end);
+}
+
+void
+queue_shuffle_order(struct queue *queue)
+{
+ queue_shuffle_order_range_with_priority(queue, 0, queue->length);
+}
+
+static void
+queue_shuffle_order_first(struct queue *queue, unsigned start, unsigned end)
+{
+ queue_swap_order(queue, start,
+ g_rand_int_range(queue->rand, start, end));
}
void
@@ -337,3 +472,132 @@ queue_shuffle_range(struct queue *queue, unsigned start, unsigned end)
queue_swap(queue, i, ri);
}
}
+
+/**
+ * Find the first item that has this specified priority or higher.
+ */
+G_GNUC_PURE
+static unsigned
+queue_find_priority_order(const struct queue *queue, unsigned start_order,
+ uint8_t priority, unsigned exclude_order)
+{
+ assert(queue != NULL);
+ assert(queue->random);
+ assert(start_order <= queue->length);
+
+ for (unsigned order = start_order; order < queue->length; ++order) {
+ const unsigned position = queue_order_to_position(queue, order);
+ const struct queue_item *item = &queue->items[position];
+ if (item->priority <= priority && order != exclude_order)
+ return order;
+ }
+
+ return queue->length;
+}
+
+G_GNUC_PURE
+static unsigned
+queue_count_same_priority(const struct queue *queue, unsigned start_order,
+ uint8_t priority)
+{
+ assert(queue != NULL);
+ assert(queue->random);
+ assert(start_order <= queue->length);
+
+ for (unsigned order = start_order; order < queue->length; ++order) {
+ const unsigned position = queue_order_to_position(queue, order);
+ const struct queue_item *item = &queue->items[position];
+ if (item->priority != priority)
+ return order - start_order;
+ }
+
+ return queue->length - start_order;
+}
+
+bool
+queue_set_priority(struct queue *queue, unsigned position, uint8_t priority,
+ int after_order)
+{
+ assert(queue != NULL);
+ assert(position < queue->length);
+
+ struct queue_item *item = &queue->items[position];
+ uint8_t old_priority = item->priority;
+ if (old_priority == priority)
+ return false;
+
+ item->version = queue->version;
+ item->priority = priority;
+
+ if (!queue->random)
+ /* don't reorder if not in random mode */
+ return true;
+
+ unsigned order = queue_position_to_order(queue, position);
+ if (after_order >= 0) {
+ if (order == (unsigned)after_order)
+ /* don't reorder the current song */
+ return true;
+
+ if (order < (unsigned)after_order) {
+ /* the specified song has been played already
+ - enqueue it only if its priority has just
+ become bigger than the current one's */
+
+ const unsigned after_position =
+ queue_order_to_position(queue, after_order);
+ const struct queue_item *after_item =
+ &queue->items[after_position];
+ if (old_priority > after_item->priority ||
+ priority <= after_item->priority)
+ /* priority hasn't become bigger */
+ return true;
+ }
+ }
+
+ /* move the item to the beginning of the priority group (or
+ create a new priority group) */
+
+ const unsigned before_order =
+ queue_find_priority_order(queue, after_order + 1, priority,
+ order);
+ const unsigned new_order = before_order > order
+ ? before_order - 1
+ : before_order;
+ queue_move_order(queue, order, new_order);
+
+ /* shuffle the song within that priority group */
+
+ const unsigned priority_count =
+ queue_count_same_priority(queue, new_order, priority);
+ assert(priority_count >= 1);
+ queue_shuffle_order_first(queue, new_order,
+ new_order + priority_count);
+
+ return true;
+}
+
+bool
+queue_set_priority_range(struct queue *queue,
+ unsigned start_position, unsigned end_position,
+ uint8_t priority, int after_order)
+{
+ assert(queue != NULL);
+ assert(start_position <= end_position);
+ assert(end_position <= queue->length);
+
+ bool modified = false;
+ int after_position = after_order >= 0
+ ? (int)queue_order_to_position(queue, after_order)
+ : -1;
+ for (unsigned i = start_position; i < end_position; ++i) {
+ after_order = after_position >= 0
+ ? (int)queue_position_to_order(queue, after_position)
+ : -1;
+
+ modified |= queue_set_priority(queue, i, priority,
+ after_order);
+ }
+
+ return modified;
+}
diff --git a/src/queue.h b/src/queue.h
index 05eeafa22..5cb5c196b 100644
--- a/src/queue.h
+++ b/src/queue.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -46,6 +46,13 @@ struct queue_item {
/** when was this item last changed? */
uint32_t version;
+
+ /**
+ * The priority of this item, between 0 and 255. High
+ * priority value means that this song gets played first in
+ * "random" mode.
+ */
+ uint8_t priority;
};
/**
@@ -181,6 +188,15 @@ queue_position_to_order(const struct queue *queue, unsigned position)
}
}
+G_GNUC_PURE
+static inline uint8_t
+queue_get_priority_at_position(const struct queue *queue, unsigned position)
+{
+ assert(position < queue->length);
+
+ return queue->items[position].priority;
+}
+
/**
* Returns the song at the specified position.
*/
@@ -320,6 +336,14 @@ queue_restore_order(struct queue *queue)
}
/**
+ * Shuffle the order of items in the specified range, taking their
+ * priorities into account.
+ */
+void
+queue_shuffle_order_range_with_priority(struct queue *queue,
+ unsigned start, unsigned end);
+
+/**
* Shuffles the virtual order of songs, but does not move them
* physically. This is used in random mode.
*/
@@ -341,4 +365,13 @@ queue_shuffle_order_last(struct queue *queue, unsigned start, unsigned end);
void
queue_shuffle_range(struct queue *queue, unsigned start, unsigned end);
+bool
+queue_set_priority(struct queue *queue, unsigned position,
+ uint8_t priority, int after_order);
+
+bool
+queue_set_priority_range(struct queue *queue,
+ unsigned start_position, unsigned end_position,
+ uint8_t priority, int after_order);
+
#endif
diff --git a/src/queue_print.c b/src/queue_print.c
index 53ddfb689..d149e8b6f 100644
--- a/src/queue_print.c
+++ b/src/queue_print.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -41,6 +41,10 @@ queue_print_song_info(struct client *client, const struct queue *queue,
song_print_info(client, queue_get(queue, position));
client_printf(client, "Pos: %u\nId: %u\n",
position, queue_position_to_id(queue, position));
+
+ uint8_t priority = queue_get_priority_at_position(queue, position);
+ if (priority != 0)
+ client_printf(client, "Prio: %u\n", priority);
}
void
diff --git a/src/queue_print.h b/src/queue_print.h
index d754a9673..371e20416 100644
--- a/src/queue_print.h
+++ b/src/queue_print.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/queue_save.c b/src/queue_save.c
index afe04ca2d..a7c511c0e 100644
--- a/src/queue_save.c
+++ b/src/queue_save.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/queue_save.h b/src/queue_save.h
index 287683390..5526d615d 100644
--- a/src/queue_save.h
+++ b/src/queue_save.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/refcount.h b/src/refcount.h
index 87a2715a4..a0e0a30b7 100644
--- a/src/refcount.h
+++ b/src/refcount.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* Redistribution and use in source and binary forms, with or without
diff --git a/src/replay_gain_ape.c b/src/replay_gain_ape.c
index 9ae47468f..0b59e3c02 100644
--- a/src/replay_gain_ape.c
+++ b/src/replay_gain_ape.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/replay_gain_ape.h b/src/replay_gain_ape.h
index 8525ac85e..35760a0aa 100644
--- a/src/replay_gain_ape.h
+++ b/src/replay_gain_ape.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/replay_gain_config.c b/src/replay_gain_config.c
index bbfe127a7..2181387b7 100644
--- a/src/replay_gain_config.c
+++ b/src/replay_gain_config.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/replay_gain_config.h b/src/replay_gain_config.h
index 8fb77a5f6..18747cef2 100644
--- a/src/replay_gain_config.h
+++ b/src/replay_gain_config.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/replay_gain_info.c b/src/replay_gain_info.c
index 3b4ab4577..1f09e7a1a 100644
--- a/src/replay_gain_info.c
+++ b/src/replay_gain_info.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/replay_gain_info.h b/src/replay_gain_info.h
index 83b46df84..9097c3e02 100644
--- a/src/replay_gain_info.h
+++ b/src/replay_gain_info.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/riff.c b/src/riff.c
index 2e8648ff6..670fb55f5 100644
--- a/src/riff.c
+++ b/src/riff.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/riff.h b/src/riff.h
index bfcb69a7d..7b35e092a 100644
--- a/src/riff.h
+++ b/src/riff.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/rtsp_client.c b/src/rtsp_client.c
new file mode 100644
index 000000000..a808ef207
--- /dev/null
+++ b/src/rtsp_client.c
@@ -0,0 +1,731 @@
+/*
+ * 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.
+ */
+
+/*
+ * Based on the RTSP client by Shiro Ninomiya <shiron@snino.com>
+ */
+
+#include "rtsp_client.h"
+#include "tcp_socket.h"
+#include "glib_compat.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#ifdef WIN32
+#define WINVER 0x0501
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#endif
+
+/*
+ * Free all memory associated with key_data
+ */
+void
+free_kd(struct key_data *kd)
+{
+ struct key_data *iter = kd;
+ while (iter) {
+ g_free(iter->key);
+ g_free(iter->data);
+ iter = iter->next;
+ g_free(kd);
+ kd = iter;
+ }
+}
+
+/*
+ * key_data type data look up
+ */
+char *
+kd_lookup(struct key_data *kd, const char *key)
+{
+ while (kd) {
+ if (!strcmp(kd->key, key)) {
+ return kd->data;
+ }
+ kd = kd->next;
+ }
+ return NULL;
+}
+
+struct rtspcl_data *
+rtspcl_open(void)
+{
+ struct rtspcl_data *rtspcld;
+ rtspcld = g_new0(struct rtspcl_data, 1);
+ rtspcld->mutex = g_mutex_new();
+ rtspcld->cond = g_cond_new();
+ rtspcld->received_lines = g_queue_new();
+ rtspcld->useragent = "RTSPClient";
+ return rtspcld;
+}
+
+/* bind an opened socket to specified hostname and port.
+ * if hostname=NULL, use INADDR_ANY.
+ * if *port=0, use dynamically assigned port
+ */
+static int bind_host(int sd, char *hostname, unsigned long ulAddr,
+ unsigned short *port, GError **error_r)
+{
+ struct sockaddr_in my_addr;
+ socklen_t nlen = sizeof(struct sockaddr);
+ struct hostent *h;
+
+ memset(&my_addr, 0, sizeof(my_addr));
+ /* use specified hostname */
+ if (hostname) {
+ /* get server IP address (no check if input is IP address or DNS name) */
+ h = gethostbyname(hostname);
+ if (h == NULL) {
+ if (strstr(hostname, "255.255.255.255") == hostname) {
+ my_addr.sin_addr.s_addr=-1;
+ } else {
+ if ((my_addr.sin_addr.s_addr = inet_addr(hostname)) == 0xFFFFFFFF) {
+ g_set_error(error_r, rtsp_client_quark(), 0,
+ "failed to resolve host '%s'",
+ hostname);
+ return -1;
+ }
+ }
+ my_addr.sin_family = AF_INET;
+ } else {
+ my_addr.sin_family = h->h_addrtype;
+ memcpy((char *) &my_addr.sin_addr.s_addr,
+ h->h_addr_list[0], h->h_length);
+ }
+ } else {
+ // if hostname=NULL, use INADDR_ANY
+ if (ulAddr)
+ my_addr.sin_addr.s_addr = ulAddr;
+ else
+ my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ my_addr.sin_family = AF_INET;
+ }
+
+ /* bind a specified port */
+ my_addr.sin_port = htons(*port);
+
+ if (bind(sd, (struct sockaddr *) &my_addr, sizeof(my_addr)) < 0) {
+ g_set_error(error_r, rtsp_client_quark(), errno,
+ "failed to bind socket: %s",
+ g_strerror(errno));
+ return -1;
+ }
+
+ if (*port == 0) {
+ getsockname(sd, (struct sockaddr *) &my_addr, &nlen);
+ *port = ntohs(my_addr.sin_port);
+ }
+
+ return 0;
+}
+
+/*
+ * open tcp port
+ */
+static int
+open_tcp_socket(char *hostname, unsigned short *port,
+ GError **error_r)
+{
+ int sd;
+
+ /* socket creation */
+ sd = socket(AF_INET, SOCK_STREAM, 0);
+ if (sd < 0) {
+ g_set_error(error_r, rtsp_client_quark(), errno,
+ "failed to create TCP socket: %s",
+ g_strerror(errno));
+ return -1;
+ }
+ if (bind_host(sd, hostname, 0, port, error_r)) {
+ close(sd);
+ return -1;
+ }
+
+ return sd;
+}
+
+static bool
+get_sockaddr_by_host(const char *host, short destport,
+ struct sockaddr_in *addr,
+ GError **error_r)
+{
+ struct hostent *h;
+
+ h = gethostbyname(host);
+ if (h) {
+ addr->sin_family = h->h_addrtype;
+ memcpy((char *) &addr->sin_addr.s_addr, h->h_addr_list[0], h->h_length);
+ } else {
+ addr->sin_family = AF_INET;
+ if ((addr->sin_addr.s_addr=inet_addr(host))==0xFFFFFFFF) {
+ g_set_error(error_r, rtsp_client_quark(), 0,
+ "failed to resolve host '%s'", host);
+ return false;
+ }
+ }
+ addr->sin_port = htons(destport);
+ return true;
+}
+
+/*
+ * create tcp connection
+ * as long as the socket is not non-blocking, this can block the process
+ * nsport is network byte order
+ */
+static bool
+get_tcp_connect(int sd, struct sockaddr_in dest_addr, GError **error_r)
+{
+ if (connect(sd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr))){
+ g_usleep(100000);
+ // try one more time
+ if (connect(sd, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr))) {
+ g_set_error(error_r, rtsp_client_quark(), errno,
+ "failed to connect to %s:%d: %s",
+ inet_ntoa(dest_addr.sin_addr),
+ ntohs(dest_addr.sin_port),
+ g_strerror(errno));
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool
+get_tcp_connect_by_host(int sd, const char *host, short destport,
+ GError **error_r)
+{
+ struct sockaddr_in addr;
+
+ return get_sockaddr_by_host(host, destport, &addr, error_r) &&
+ get_tcp_connect(sd, addr, error_r);
+}
+
+static void
+rtsp_client_flush_received(struct rtspcl_data *rtspcld)
+{
+ char *line;
+ while ((line = g_queue_pop_head(rtspcld->received_lines)) != NULL)
+ g_free(line);
+}
+
+static size_t
+rtsp_client_socket_data(const void *_data, size_t length, void *ctx)
+{
+ struct rtspcl_data *rtspcld = ctx;
+
+ g_mutex_lock(rtspcld->mutex);
+
+ if (rtspcld->tcp_socket == NULL) {
+ g_mutex_unlock(rtspcld->mutex);
+ return 0;
+ }
+
+ const bool was_empty = g_queue_is_empty(rtspcld->received_lines);
+ bool added = false;
+ const char *data = _data, *end = data + length, *p = data, *eol;
+ while ((eol = memchr(p, '\n', end - p)) != NULL) {
+ const char *next = eol + 1;
+
+ if (rtspcld->received_lines->length < 64) {
+ if (eol > p && eol[-1] == '\r')
+ --eol;
+
+ g_queue_push_tail(rtspcld->received_lines,
+ g_strndup(p, eol - p));
+ added = true;
+ }
+
+ p = next;
+ }
+
+ if (was_empty && added)
+ g_cond_broadcast(rtspcld->cond);
+
+ g_mutex_unlock(rtspcld->mutex);
+
+ return p - data;
+}
+
+static void
+rtsp_client_socket_error(GError *error, void *ctx)
+{
+ struct rtspcl_data *rtspcld = ctx;
+
+ g_warning("%s", error->message);
+ g_error_free(error);
+
+ g_mutex_lock(rtspcld->mutex);
+
+ rtsp_client_flush_received(rtspcld);
+
+ struct tcp_socket *s = rtspcld->tcp_socket;
+ rtspcld->tcp_socket = NULL;
+
+ g_cond_broadcast(rtspcld->cond);
+
+ g_mutex_unlock(rtspcld->mutex);
+
+ if (s != NULL)
+ tcp_socket_free(s);
+}
+
+static void
+rtsp_client_socket_disconnected(void *ctx)
+{
+ struct rtspcl_data *rtspcld = ctx;
+
+ g_mutex_lock(rtspcld->mutex);
+
+ rtsp_client_flush_received(rtspcld);
+
+ struct tcp_socket *s = rtspcld->tcp_socket;
+ rtspcld->tcp_socket = NULL;
+
+ g_cond_broadcast(rtspcld->cond);
+
+ g_mutex_unlock(rtspcld->mutex);
+
+ if (s != NULL)
+ tcp_socket_free(s);
+}
+
+static const struct tcp_socket_handler rtsp_client_socket_handler = {
+ .data = rtsp_client_socket_data,
+ .error = rtsp_client_socket_error,
+ .disconnected = rtsp_client_socket_disconnected,
+};
+
+bool
+rtspcl_connect(struct rtspcl_data *rtspcld, const char *host, short destport,
+ const char *sid, GError **error_r)
+{
+ assert(rtspcld->tcp_socket == NULL);
+
+ unsigned short myport = 0;
+ struct sockaddr_in name;
+ socklen_t namelen = sizeof(name);
+
+ int fd = open_tcp_socket(NULL, &myport, error_r);
+ if (fd < 0)
+ return false;
+
+ if (!get_tcp_connect_by_host(fd, host, destport, error_r))
+ return false;
+
+ getsockname(fd, (struct sockaddr*)&name, &namelen);
+ memcpy(&rtspcld->local_addr, &name.sin_addr,sizeof(struct in_addr));
+ sprintf(rtspcld->url, "rtsp://%s/%s", inet_ntoa(name.sin_addr), sid);
+ getpeername(fd, (struct sockaddr*)&name, &namelen);
+ memcpy(&rtspcld->host_addr, &name.sin_addr, sizeof(struct in_addr));
+
+ rtspcld->tcp_socket = tcp_socket_new(fd, &rtsp_client_socket_handler,
+ rtspcld);
+
+ return true;
+}
+
+static void
+rtspcl_disconnect(struct rtspcl_data *rtspcld)
+{
+ g_mutex_lock(rtspcld->mutex);
+ rtsp_client_flush_received(rtspcld);
+ g_mutex_unlock(rtspcld->mutex);
+
+ if (rtspcld->tcp_socket != NULL) {
+ tcp_socket_free(rtspcld->tcp_socket);
+ rtspcld->tcp_socket = NULL;
+ }
+}
+
+static void
+rtspcl_remove_all_exthds(struct rtspcl_data *rtspcld)
+{
+ free_kd(rtspcld->exthds);
+ rtspcld->exthds = NULL;
+}
+
+void
+rtspcl_close(struct rtspcl_data *rtspcld)
+{
+ rtspcl_disconnect(rtspcld);
+ g_queue_free(rtspcld->received_lines);
+ rtspcl_remove_all_exthds(rtspcld);
+ g_free(rtspcld->session);
+ g_cond_free(rtspcld->cond);
+ g_mutex_free(rtspcld->mutex);
+ g_free(rtspcld);
+}
+
+void
+rtspcl_add_exthds(struct rtspcl_data *rtspcld, const char *key, char *data)
+{
+ struct key_data *new_kd;
+ new_kd = g_new(struct key_data, 1);
+ new_kd->key = g_strdup(key);
+ new_kd->data = g_strdup(data);
+ new_kd->next = NULL;
+ if (!rtspcld->exthds) {
+ rtspcld->exthds = new_kd;
+ } else {
+ struct key_data *iter = rtspcld->exthds;
+ while (iter->next) {
+ iter = iter->next;
+ }
+ iter->next = new_kd;
+ }
+}
+
+/*
+ * read one line from the file descriptor
+ * timeout: msec unit, -1 for infinite
+ * if CR comes then following LF is expected
+ * returned string in line is always null terminated, maxlen-1 is maximum string length
+ */
+static int
+read_line(struct rtspcl_data *rtspcld, char *line, int maxlen,
+ int timeout)
+{
+ g_mutex_lock(rtspcld->mutex);
+
+ GTimeVal end_time;
+ if (timeout >= 0) {
+ g_get_current_time(&end_time);
+
+ end_time.tv_sec += timeout / 1000;
+ timeout %= 1000;
+ end_time.tv_usec = timeout * 1000;
+ if (end_time.tv_usec > 1000000) {
+ end_time.tv_usec -= 1000000;
+ ++end_time.tv_sec;
+ }
+ }
+
+ while (true) {
+ if (!g_queue_is_empty(rtspcld->received_lines)) {
+ /* success, copy to buffer */
+
+ char *p = g_queue_pop_head(rtspcld->received_lines);
+ g_mutex_unlock(rtspcld->mutex);
+
+ g_strlcpy(line, p, maxlen);
+ g_free(p);
+
+ return strlen(line);
+ }
+
+ if (rtspcld->tcp_socket == NULL) {
+ /* error */
+ g_mutex_unlock(rtspcld->mutex);
+ return -1;
+ }
+
+ if (timeout < 0) {
+ g_cond_wait(rtspcld->cond, rtspcld->mutex);
+ } else if (!g_cond_timed_wait(rtspcld->cond, rtspcld->mutex,
+ &end_time)) {
+ g_mutex_unlock(rtspcld->mutex);
+ return 0;
+ }
+ }
+}
+
+/*
+ * send RTSP request, and get response if it's needed
+ * if this gets a success, *kd is allocated or reallocated (if *kd is not NULL)
+ */
+bool
+exec_request(struct rtspcl_data *rtspcld, const char *cmd,
+ const char *content_type, const char *content,
+ int get_response,
+ const struct key_data *hds, struct key_data **kd,
+ GError **error_r)
+{
+ char line[1024];
+ char req[1024];
+ char reql[128];
+ const char delimiters[] = " ";
+ char *token, *dp;
+ int dsize = 0;
+ int timeout = 5000; // msec unit
+
+ if (!rtspcld) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "not connected");
+ return false;
+ }
+
+ sprintf(req, "%s %s RTSP/1.0\r\nCSeq: %d\r\n", cmd, rtspcld->url, ++rtspcld->cseq );
+
+ if ( rtspcld->session != NULL ) {
+ sprintf(reql,"Session: %s\r\n", rtspcld->session );
+ strncat(req,reql,sizeof(req));
+ }
+
+ const struct key_data *hd_iter = hds;
+ while (hd_iter) {
+ sprintf(reql, "%s: %s\r\n", hd_iter->key, hd_iter->data);
+ strncat(req, reql, sizeof(req));
+ hd_iter = hd_iter->next;
+ }
+
+ if (content_type && content) {
+ sprintf(reql, "Content-Type: %s\r\nContent-Length: %d\r\n",
+ content_type, (int) strlen(content));
+ strncat(req,reql,sizeof(req));
+ }
+
+ sprintf(reql, "User-Agent: %s\r\n", rtspcld->useragent);
+ strncat(req, reql, sizeof(req));
+
+ hd_iter = rtspcld->exthds;
+ while (hd_iter) {
+ sprintf(reql, "%s: %s\r\n", hd_iter->key, hd_iter->data);
+ strncat(req, reql, sizeof(req));
+ hd_iter = hd_iter->next;
+ }
+ strncat(req, "\r\n", sizeof(req));
+
+ if (content_type && content)
+ strncat(req, content, sizeof(req));
+
+ if (!tcp_socket_send(rtspcld->tcp_socket, req, strlen(req))) {
+ g_set_error(error_r, rtsp_client_quark(), errno,
+ "write error: %s",
+ g_strerror(errno));
+ return false;
+ }
+
+ if (!get_response) return true;
+
+ if (read_line(rtspcld, line, sizeof(line), timeout) <= 0) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "request failed");
+ return false;
+ }
+
+ token = strtok(line, delimiters);
+ token = strtok(NULL, delimiters);
+ if (token == NULL) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "request failed");
+ return false;
+ }
+
+ if (strcmp(token, "200") != 0) {
+ g_set_error(error_r, rtsp_client_quark(), 0,
+ "request failed: %s", token);
+ return false;
+ }
+
+ /* if the caller isn't interested in response headers, put
+ them on the trash, which is freed before returning from
+ this function */
+ struct key_data *trash = NULL;
+ if (kd == NULL)
+ kd = &trash;
+
+ struct key_data *cur_kd = *kd;
+
+ struct key_data *new_kd = NULL;
+ while (read_line(rtspcld, line, sizeof(line), timeout) > 0) {
+ timeout = 1000; // once it started, it shouldn't take a long time
+ if (new_kd != NULL && line[0] == ' ') {
+ const char *j = line;
+ while (*j == ' ')
+ ++j;
+
+ dsize += strlen(j);
+ new_kd->data = g_realloc(new_kd->data, dsize);
+ strcat(new_kd->data, j);
+ continue;
+ }
+ dp = strstr(line, ":");
+ if (!dp) {
+ free_kd(*kd);
+ *kd = NULL;
+
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "request failed, bad header");
+ return false;
+ }
+
+ *dp++ = 0;
+ new_kd = g_new(struct key_data, 1);
+ new_kd->key = g_strdup(line);
+ dsize = strlen(dp) + 1;
+ new_kd->data = g_strdup(dp);
+ new_kd->next = NULL;
+ if (cur_kd == NULL) {
+ cur_kd = *kd = new_kd;
+ } else {
+ cur_kd->next = new_kd;
+ cur_kd = new_kd;
+ }
+ }
+
+ free_kd(trash);
+
+ return true;
+}
+
+bool
+rtspcl_set_parameter(struct rtspcl_data *rtspcld, const char *parameter,
+ GError **error_r)
+{
+ return exec_request(rtspcld, "SET_PARAMETER", "text/parameters",
+ parameter, 1, NULL, NULL, error_r);
+}
+
+void
+rtspcl_set_useragent(struct rtspcl_data *rtspcld, const char *name)
+{
+ rtspcld->useragent = name;
+}
+
+bool
+rtspcl_announce_sdp(struct rtspcl_data *rtspcld, const char *sdp,
+ GError **error_r)
+{
+ return exec_request(rtspcld, "ANNOUNCE", "application/sdp", sdp, 1,
+ NULL, NULL, error_r);
+}
+
+bool
+rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd,
+ int control_port, int ntp_port,
+ GError **error_r)
+{
+ struct key_data *rkd = NULL, hds;
+ const char delimiters[] = ";";
+ char *buf = NULL;
+ char *token, *pc;
+ int rval = false;
+
+ static char transport_key[] = "Transport";
+
+ char transport_value[256];
+ snprintf(transport_value, sizeof(transport_value),
+ "RTP/AVP/UDP;unicast;interleaved=0-1;mode=record;control_port=%d;timing_port=%d",
+ control_port, ntp_port);
+
+ hds.key = transport_key;
+ hds.data = transport_value;
+ hds.next = NULL;
+ if (!exec_request(rtspcld, "SETUP", NULL, NULL, 1,
+ &hds, &rkd, error_r))
+ return false;
+
+ if (!(rtspcld->session = g_strdup(kd_lookup(rkd, "Session")))) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "no session in response");
+ goto erexit;
+ }
+ if (!(rtspcld->transport = kd_lookup(rkd, "Transport"))) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "no transport in response");
+ goto erexit;
+ }
+ buf = g_strdup(rtspcld->transport);
+ token = strtok(buf, delimiters);
+ rtspcld->server_port = 0;
+ rtspcld->control_port = 0;
+ while (token) {
+ if ((pc = strstr(token, "="))) {
+ *pc = 0;
+ if (!strcmp(token,"server_port")) {
+ rtspcld->server_port=atoi(pc + 1);
+ }
+ if (!strcmp(token,"control_port")) {
+ rtspcld->control_port=atoi(pc + 1);
+ }
+ }
+ token = strtok(NULL, delimiters);
+ }
+ if (rtspcld->server_port == 0) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "no server_port in response");
+ goto erexit;
+ }
+ if (rtspcld->control_port == 0) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "no control_port in response");
+ goto erexit;
+ }
+ rval = true;
+ erexit:
+ g_free(buf);
+
+ if (!rval || kd == NULL) {
+ free_kd(rkd);
+ rkd = NULL;
+ }
+
+ if (kd != NULL)
+ *kd = rkd;
+
+ return rval;
+}
+
+bool
+rtspcl_record(struct rtspcl_data *rtspcld,
+ int seq_num, int rtptime,
+ GError **error_r)
+{
+ if (!rtspcld->session) {
+ g_set_error_literal(error_r, rtsp_client_quark(), 0,
+ "no session in progress");
+ return false;
+ }
+
+ char buf[128];
+ sprintf(buf, "seq=%d,rtptime=%u", seq_num, rtptime);
+
+ struct key_data rtp;
+ static char rtp_key[] = "RTP-Info";
+ rtp.key = rtp_key;
+ rtp.data = buf;
+ rtp.next = NULL;
+
+ struct key_data range;
+ static char range_key[] = "Range";
+ range.key = range_key;
+ static char range_value[] = "npt=0-";
+ range.data = range_value;
+ range.next = &rtp;
+
+ return exec_request(rtspcld, "RECORD", NULL, NULL, 1, &range,
+ NULL, error_r);
+}
+
+char *
+rtspcl_local_ip(struct rtspcl_data *rtspcld)
+{
+ return inet_ntoa(rtspcld->local_addr);
+}
diff --git a/src/rtsp_client.h b/src/rtsp_client.h
new file mode 100644
index 000000000..5c8425248
--- /dev/null
+++ b/src/rtsp_client.h
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+/*
+ * Based on the RTSP client by Shiro Ninomiya <shiron@snino.com>
+ */
+
+#ifndef MPD_RTSP_CLIENT_H
+#define MPD_RTSP_CLIENT_H
+
+#include <stdbool.h>
+#include <glib.h>
+
+#ifdef WIN32
+#define WINVER 0x0501
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <netinet/in.h>
+#endif
+
+struct key_data {
+ char *key;
+ char *data;
+ struct key_data *next;
+};
+
+struct rtspcl_data {
+ GMutex *mutex;
+ GCond *cond;
+
+ GQueue *received_lines;
+
+ struct tcp_socket *tcp_socket;
+
+ char url[128];
+ int cseq;
+ struct key_data *exthds;
+ char *session;
+ char *transport;
+ unsigned short server_port;
+ unsigned short control_port;
+ struct in_addr host_addr;
+ struct in_addr local_addr;
+ const char *useragent;
+
+};
+
+/**
+ * The quark used for GError.domain.
+ */
+static inline GQuark
+rtsp_client_quark(void)
+{
+ return g_quark_from_static_string("rtsp_client");
+}
+
+void
+free_kd(struct key_data *kd);
+
+char *
+kd_lookup(struct key_data *kd, const char *key);
+
+G_GNUC_MALLOC
+struct rtspcl_data *
+rtspcl_open(void);
+
+bool
+rtspcl_connect(struct rtspcl_data *rtspcld, const char *host, short destport,
+ const char *sid, GError **error_r);
+
+void
+rtspcl_close(struct rtspcl_data *rtspcld);
+
+void
+rtspcl_add_exthds(struct rtspcl_data *rtspcld, const char *key, char *data);
+
+bool
+exec_request(struct rtspcl_data *rtspcld, const char *cmd,
+ const char *content_type, const char *content,
+ int get_response,
+ const struct key_data *hds, struct key_data **kd,
+ GError **error_r);
+
+bool
+rtspcl_set_parameter(struct rtspcl_data *rtspcld, const char *parameter,
+ GError **error_r);
+
+void
+rtspcl_set_useragent(struct rtspcl_data *rtspcld, const char *name);
+
+bool
+rtspcl_announce_sdp(struct rtspcl_data *rtspcld, const char *sdp,
+ GError **error_r);
+
+bool
+rtspcl_setup(struct rtspcl_data *rtspcld, struct key_data **kd,
+ int control_port, int ntp_port,
+ GError **error_r);
+
+bool
+rtspcl_record(struct rtspcl_data *rtspcld,
+ int seq_num, int rtptime,
+ GError **error_r);
+
+char *
+rtspcl_local_ip(struct rtspcl_data *rtspcld);
+
+#endif
diff --git a/src/server_socket.c b/src/server_socket.c
index bb7a6f097..82ad81f12 100644
--- a/src/server_socket.c
+++ b/src/server_socket.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,11 @@
*/
#include "config.h"
+
+#ifdef HAVE_STRUCT_UCRED
+#define _GNU_SOURCE 1
+#endif
+
#include "server_socket.h"
#include "socket_util.h"
#include "fd_util.h"
diff --git a/src/server_socket.h b/src/server_socket.h
index ae0ce0c8d..e5777f7b3 100644
--- a/src/server_socket.h
+++ b/src/server_socket.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -47,7 +47,7 @@ server_socket_close(struct server_socket *ss);
* Add a listener on a port on all interfaces.
*
* @param port the TCP port
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
* @return true on success
*/
@@ -61,7 +61,7 @@ server_socket_add_port(struct server_socket *ss, unsigned port,
*
* @param hostname the host name to be resolved
* @param port the TCP port
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
* @return true on success
*/
@@ -73,7 +73,7 @@ server_socket_add_host(struct server_socket *ss, const char *hostname,
* Add a listener on a Unix domain socket.
*
* @param path the absolute socket path
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
* @return true on success
*/
diff --git a/src/sig_handlers.c b/src/sig_handlers.c
index 8aa85cf88..b23f9e778 100644
--- a/src/sig_handlers.c
+++ b/src/sig_handlers.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/sig_handlers.h b/src/sig_handlers.h
index a578cd243..32e9bad95 100644
--- a/src/sig_handlers.h
+++ b/src/sig_handlers.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/socket_util.c b/src/socket_util.c
index 0909765ba..a89a67ed6 100644
--- a/src/socket_util.c
+++ b/src/socket_util.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/socket_util.h b/src/socket_util.h
index 7ef081362..3ebf4084c 100644
--- a/src/socket_util.h
+++ b/src/socket_util.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -37,7 +37,7 @@ struct sockaddr;
*
* @param sa the sockaddr struct
* @param length the length of #sa in bytes
- * @param error location to store the error occuring, or NULL to
+ * @param error location to store the error occurring, or NULL to
* ignore errors
*/
char *
@@ -53,7 +53,7 @@ sockaddr_to_string(const struct sockaddr *sa, size_t length, GError **error);
* @param address the address to listen on
* @param address_length the size of #address
* @param backlog the backlog parameter for the listen() system call
- * @param error location to store the error occuring, or NULL to
+ * @param error location to store the error occurring, or NULL to
* ignore errors
* @return the socket file descriptor or -1 on error
*/
diff --git a/src/song.c b/src/song.c
index 13fd476b9..bddf8eb83 100644
--- a/src/song.c
+++ b/src/song.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/song.h b/src/song.h
index 26a1dc806..9553c76c4 100644
--- a/src/song.h
+++ b/src/song.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/song_print.c b/src/song_print.c
index 16239e03b..2065b336d 100644
--- a/src/song_print.c
+++ b/src/song_print.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -94,18 +94,3 @@ song_print_info(struct client *client, struct song *song)
if (song->tag)
tag_print(client, song->tag);
}
-
-static int
-song_print_info_x(struct song *song, void *data)
-{
- struct client *client = data;
- song_print_info(client, song);
-
- return 0;
-}
-
-void
-songvec_print(struct client *client, const struct songvec *sv)
-{
- songvec_for_each(sv, song_print_info_x, client);
-}
diff --git a/src/song_print.h b/src/song_print.h
index cb83f4711..8f1f0cc65 100644
--- a/src/song_print.h
+++ b/src/song_print.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -28,9 +28,6 @@ void
song_print_info(struct client *client, struct song *song);
void
-songvec_print(struct client *client, const struct songvec *sv);
-
-void
song_print_uri(struct client *client, struct song *song);
#endif
diff --git a/src/song_save.c b/src/song_save.c
index a1a573298..5c9353628 100644
--- a/src/song_save.c
+++ b/src/song_save.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,6 +24,7 @@
#include "directory.h"
#include "tag.h"
#include "text_file.h"
+#include "string_util.h"
#include <glib.h>
@@ -96,7 +97,7 @@ song_load(FILE *fp, struct directory *parent, const char *uri,
}
*colon++ = 0;
- value = g_strchug(colon);
+ value = strchug_fast_c(colon);
if ((type = tag_name_parse(line)) != TAG_NUM_OF_ITEM_TYPES) {
if (!song->tag) {
diff --git a/src/song_save.h b/src/song_save.h
index 03285015a..1aaa7642c 100644
--- a/src/song_save.h
+++ b/src/song_save.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -40,7 +40,7 @@ songvec_save(FILE *fp, const struct songvec *sv);
* Loads a song from the input file. Reading stops after the
* "song_end" line.
*
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
* @return true on success, false on error
*/
diff --git a/src/song_sticker.c b/src/song_sticker.c
index c3c64c8d1..78025906e 100644
--- a/src/song_sticker.c
+++ b/src/song_sticker.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/song_sticker.h b/src/song_sticker.h
index 6318ccf48..43fe59dbd 100644
--- a/src/song_sticker.h
+++ b/src/song_sticker.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/song_update.c b/src/song_update.c
index b418b600e..e2a845eef 100644
--- a/src/song_update.c
+++ b/src/song_update.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -190,7 +190,7 @@ song_file_update_inarchive(struct song *song)
tag_free(song->tag);
//accept every file that has music suffix
- //because we dont support tag reading throught
+ //because we don't support tag reading through
//input streams
song->tag = tag_new();
diff --git a/src/songvec.c b/src/songvec.c
index 38bcbac88..7d5a7a474 100644
--- a/src/songvec.c
+++ b/src/songvec.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/songvec.h b/src/songvec.h
index 8a50b974b..521a37700 100644
--- a/src/songvec.h
+++ b/src/songvec.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/state_file.c b/src/state_file.c
index 55af25d5c..d7dde6583 100644
--- a/src/state_file.c
+++ b/src/state_file.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -47,7 +47,7 @@ static unsigned prev_volume_version, prev_output_version,
prev_playlist_version;
static void
-state_file_write(void)
+state_file_write(struct player_control *pc)
{
FILE *fp;
@@ -64,17 +64,17 @@ state_file_write(void)
save_sw_volume_state(fp);
audio_output_state_save(fp);
- playlist_state_save(fp, &g_playlist);
+ playlist_state_save(fp, &g_playlist, pc);
fclose(fp);
prev_volume_version = sw_volume_state_get_hash();
prev_output_version = audio_output_state_get_version();
- prev_playlist_version = playlist_state_get_hash(&g_playlist);
+ prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
}
static void
-state_file_read(void)
+state_file_read(struct player_control *pc)
{
FILE *fp;
bool success;
@@ -95,7 +95,8 @@ state_file_read(void)
while ((line = read_text_line(fp, buffer)) != NULL) {
success = read_sw_volume_state(line) ||
audio_output_state_read(line) ||
- playlist_state_restore(line, fp, buffer, &g_playlist);
+ playlist_state_restore(line, fp, buffer,
+ &g_playlist, pc);
if (!success)
g_warning("Unrecognized line in state file: %s", line);
}
@@ -104,7 +105,7 @@ state_file_read(void)
prev_volume_version = sw_volume_state_get_hash();
prev_output_version = audio_output_state_get_version();
- prev_playlist_version = playlist_state_get_hash(&g_playlist);
+ prev_playlist_version = playlist_state_get_hash(&g_playlist, pc);
g_string_free(buffer, true);
@@ -115,21 +116,23 @@ state_file_read(void)
* saves the state file.
*/
static gboolean
-timer_save_state_file(G_GNUC_UNUSED gpointer data)
+timer_save_state_file(gpointer data)
{
+ struct player_control *pc = data;
+
if (prev_volume_version == sw_volume_state_get_hash() &&
prev_output_version == audio_output_state_get_version() &&
- prev_playlist_version == playlist_state_get_hash(&g_playlist))
+ prev_playlist_version == playlist_state_get_hash(&g_playlist, pc))
/* nothing has changed - don't save the state file,
don't spin up the hard disk */
return true;
- state_file_write();
+ state_file_write(pc);
return true;
}
void
-state_file_init(const char *path)
+state_file_init(const char *path, struct player_control *pc)
{
assert(state_file_path == NULL);
@@ -137,15 +140,15 @@ state_file_init(const char *path)
return;
state_file_path = g_strdup(path);
- state_file_read();
+ state_file_read(pc);
save_state_source_id = g_timeout_add_seconds(5 * 60,
timer_save_state_file,
- NULL);
+ pc);
}
void
-state_file_finish(void)
+state_file_finish(struct player_control *pc)
{
if (state_file_path == NULL)
/* no state file configured, no cleanup required */
@@ -154,7 +157,7 @@ state_file_finish(void)
if (save_state_source_id != 0)
g_source_remove(save_state_source_id);
- state_file_write();
+ state_file_write(pc);
g_free(state_file_path);
}
diff --git a/src/state_file.h b/src/state_file.h
index ec01fcbed..4c4f881cc 100644
--- a/src/state_file.h
+++ b/src/state_file.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,11 +20,13 @@
#ifndef MPD_STATE_FILE_H
#define MPD_STATE_FILE_H
+struct player_control;
+
void
-state_file_init(const char *path);
+state_file_init(const char *path, struct player_control *pc);
void
-state_file_finish(void);
+state_file_finish(struct player_control *pc);
void write_state_file(void);
diff --git a/src/stats.c b/src/stats.c
index 673d531ec..ec39ff5b7 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,11 +20,13 @@
#include "config.h"
#include "stats.h"
#include "database.h"
+#include "db_visitor.h"
#include "tag.h"
#include "song.h"
#include "client.h"
#include "player_control.h"
#include "strset.h"
+#include "client_internal.h"
struct stats stats;
@@ -67,8 +69,9 @@ visit_tag(struct visit_data *data, const struct tag *tag)
}
}
-static int
-stats_collect_song(struct song *song, void *_data)
+static bool
+collect_stats_song(struct song *song, void *_data,
+ G_GNUC_UNUSED GError **error_r)
{
struct visit_data *data = _data;
@@ -77,9 +80,13 @@ stats_collect_song(struct song *song, void *_data)
if (song->tag != NULL)
visit_tag(data, song->tag);
- return 0;
+ return true;
}
+static const struct db_visitor collect_stats_visitor = {
+ .song = collect_stats_song,
+};
+
void stats_update(void)
{
struct visit_data data;
@@ -91,7 +98,7 @@ void stats_update(void)
data.artists = strset_new();
data.albums = strset_new();
- db_walk(NULL, stats_collect_song, NULL, &data);
+ db_walk("", &collect_stats_visitor, &data, NULL);
stats.artist_count = strset_size(data.artists);
stats.album_count = strset_size(data.albums);
@@ -114,7 +121,7 @@ int stats_print(struct client *client)
stats.album_count,
stats.song_count,
(long)g_timer_elapsed(stats.timer, NULL),
- (long)(pc_get_total_play_time() + 0.5),
+ (long)(pc_get_total_play_time(client->player_control) + 0.5),
stats.song_duration,
db_get_mtime());
return 0;
diff --git a/src/stats.h b/src/stats.h
index fbb2e4a46..a686477de 100644
--- a/src/stats.h
+++ b/src/stats.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/sticker.c b/src/sticker.c
index f6cd04346..346a827a5 100644
--- a/src/sticker.c
+++ b/src/sticker.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/sticker.h b/src/sticker.h
index 6cc0ebcee..5545206a5 100644
--- a/src/sticker.h
+++ b/src/sticker.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -51,7 +51,7 @@ struct sticker;
/**
* Opens the sticker database (if path is not NULL).
*
- * @param error_r location to store the error occuring, or NULL to
+ * @param error_r location to store the error occurring, or NULL to
* ignore errors
* @return true on success, false on error
*/
diff --git a/src/sticker_print.c b/src/sticker_print.c
index b158c8af3..65e79513c 100644
--- a/src/sticker_print.c
+++ b/src/sticker_print.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/sticker_print.h b/src/sticker_print.h
index ac542709c..7398c8083 100644
--- a/src/sticker_print.h
+++ b/src/sticker_print.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/stored_playlist.c b/src/stored_playlist.c
index cd2818522..39ba2bac1 100644
--- a/src/stored_playlist.c
+++ b/src/stored_playlist.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
#include "config.h"
#include "stored_playlist.h"
#include "playlist_save.h"
+#include "text_file.h"
#include "song.h"
#include "mapper.h"
#include "path.h"
@@ -27,6 +28,7 @@
#include "database.h"
#include "idle.h"
#include "conf.h"
+#include "glib_compat.h"
#include <assert.h>
#include <sys/types.h>
@@ -36,6 +38,8 @@
#include <string.h>
#include <errno.h>
+static const char PLAYLIST_COMMENT = '#';
+
static unsigned playlist_max_length;
bool playlist_saveAbsolutePaths = DEFAULT_PLAYLIST_SAVE_ABSOLUTE_PATHS;
@@ -69,6 +73,67 @@ spl_valid_name(const char *name_utf8)
strchr(name_utf8, '\r') == NULL;
}
+static const char *
+spl_map(GError **error_r)
+{
+ const char *path_fs = map_spl_path();
+ if (path_fs == NULL)
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_DISABLED,
+ "Stored playlists are disabled");
+
+ return path_fs;
+}
+
+static bool
+spl_check_name(const char *name_utf8, GError **error_r)
+{
+ if (!spl_valid_name(name_utf8)) {
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_BAD_NAME,
+ "Bad playlist name");
+ return false;
+ }
+
+ return true;
+}
+
+static char *
+spl_map_to_fs(const char *name_utf8, GError **error_r)
+{
+ if (spl_map(error_r) == NULL ||
+ !spl_check_name(name_utf8, error_r))
+ return NULL;
+
+ char *path_fs = map_spl_utf8_to_fs(name_utf8);
+ if (path_fs == NULL)
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_BAD_NAME,
+ "Bad playlist name");
+
+ return path_fs;
+}
+
+/**
+ * Create a GError for the current errno.
+ */
+static void
+playlist_errno(GError **error_r)
+{
+ switch (errno) {
+ case ENOENT:
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_NO_SUCH_LIST,
+ "No such playlist");
+ break;
+
+ default:
+ g_set_error_literal(error_r, g_file_error_quark(), errno,
+ g_strerror(errno));
+ break;
+ }
+}
+
static struct stored_playlist_info *
load_playlist_info(const char *parent_path_fs, const char *name_fs)
{
@@ -105,9 +170,9 @@ load_playlist_info(const char *parent_path_fs, const char *name_fs)
}
GPtrArray *
-spl_list(void)
+spl_list(GError **error_r)
{
- const char *parent_path_fs = map_spl_path();
+ const char *parent_path_fs = spl_map(error_r);
DIR *dir;
struct dirent *ent;
GPtrArray *list;
@@ -117,8 +182,11 @@ spl_list(void)
return NULL;
dir = opendir(parent_path_fs);
- if (dir == NULL)
+ if (dir == NULL) {
+ g_set_error_literal(error_r, g_file_error_quark(), errno,
+ g_strerror(errno));
return NULL;
+ }
list = g_ptr_array_new();
@@ -145,25 +213,26 @@ spl_list_free(GPtrArray *list)
g_ptr_array_free(list, true);
}
-static enum playlist_result
-spl_save(GPtrArray *list, const char *utf8path)
+static bool
+spl_save(GPtrArray *list, const char *utf8path, GError **error_r)
{
FILE *file;
- char *path_fs;
assert(utf8path != NULL);
- if (map_spl_path() == NULL)
- return PLAYLIST_RESULT_DISABLED;
+ if (spl_map(error_r) == NULL)
+ return false;
- path_fs = map_spl_utf8_to_fs(utf8path);
+ char *path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL)
- return PLAYLIST_RESULT_BAD_NAME;
+ return false;
file = fopen(path_fs, "w");
g_free(path_fs);
- if (file == NULL)
- return PLAYLIST_RESULT_ERRNO;
+ if (file == NULL) {
+ playlist_errno(error_r);
+ return false;
+ }
for (unsigned i = 0; i < list->len; ++i) {
const char *uri = g_ptr_array_index(list, i);
@@ -171,53 +240,46 @@ spl_save(GPtrArray *list, const char *utf8path)
}
fclose(file);
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
}
GPtrArray *
-spl_load(const char *utf8path)
+spl_load(const char *utf8path, GError **error_r)
{
FILE *file;
GPtrArray *list;
- char buffer[MPD_PATH_MAX];
char *path_fs;
- if (!spl_valid_name(utf8path) || map_spl_path() == NULL)
+ if (spl_map(error_r) == NULL)
return NULL;
- path_fs = map_spl_utf8_to_fs(utf8path);
+ path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL)
return NULL;
file = fopen(path_fs, "r");
g_free(path_fs);
- if (file == NULL)
+ if (file == NULL) {
+ playlist_errno(error_r);
return NULL;
+ }
list = g_ptr_array_new();
- while (fgets(buffer, sizeof(buffer), file)) {
- char *s = buffer;
-
- if (*s == PLAYLIST_COMMENT)
+ GString *buffer = g_string_sized_new(1024);
+ char *s;
+ while ((s = read_text_line(file, buffer)) != NULL) {
+ if (*s == 0 || *s == PLAYLIST_COMMENT)
continue;
- g_strchomp(buffer);
-
if (!uri_has_scheme(s)) {
char *path_utf8;
- struct song *song;
path_utf8 = map_fs_to_utf8(s);
if (path_utf8 == NULL)
continue;
- song = db_get_song(path_utf8);
- g_free(path_utf8);
- if (song == NULL)
- continue;
-
- s = song_get_uri(song);
+ s = path_utf8;
} else
s = g_strdup(s);
@@ -266,30 +328,33 @@ spl_insert_index_internal(GPtrArray *list, unsigned idx, char *uri)
g_ptr_array_index(list, idx) = uri;
}
-enum playlist_result
-spl_move_index(const char *utf8path, unsigned src, unsigned dest)
+bool
+spl_move_index(const char *utf8path, unsigned src, unsigned dest,
+ GError **error_r)
{
- GPtrArray *list;
char *uri;
- enum playlist_result result;
if (src == dest)
/* this doesn't check whether the playlist exists, but
what the hell.. */
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
- if (!(list = spl_load(utf8path)))
- return PLAYLIST_RESULT_NO_SUCH_LIST;
+ GPtrArray *list = spl_load(utf8path, error_r);
+ if (list == NULL)
+ return false;
if (src >= list->len || dest >= list->len) {
spl_free(list);
- return PLAYLIST_RESULT_BAD_RANGE;
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_BAD_RANGE,
+ "Bad range");
+ return false;
}
uri = spl_remove_index_internal(list, src);
spl_insert_index_internal(list, dest, uri);
- result = spl_save(list, utf8path);
+ bool result = spl_save(list, utf8path, error_r);
spl_free(list);
@@ -297,78 +362,72 @@ spl_move_index(const char *utf8path, unsigned src, unsigned dest)
return result;
}
-enum playlist_result
-spl_clear(const char *utf8path)
+bool
+spl_clear(const char *utf8path, GError **error_r)
{
- char *path_fs;
FILE *file;
- if (map_spl_path() == NULL)
- return PLAYLIST_RESULT_DISABLED;
+ if (spl_map(error_r) == NULL)
+ return false;
- if (!spl_valid_name(utf8path))
- return PLAYLIST_RESULT_BAD_NAME;
-
- path_fs = map_spl_utf8_to_fs(utf8path);
+ char *path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL)
- return PLAYLIST_RESULT_BAD_NAME;
+ return false;
file = fopen(path_fs, "w");
g_free(path_fs);
- if (file == NULL)
- return PLAYLIST_RESULT_ERRNO;
+ if (file == NULL) {
+ playlist_errno(error_r);
+ return false;
+ }
fclose(file);
idle_add(IDLE_STORED_PLAYLIST);
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
}
-enum playlist_result
-spl_delete(const char *name_utf8)
+bool
+spl_delete(const char *name_utf8, GError **error_r)
{
char *path_fs;
int ret;
- if (map_spl_path() == NULL)
- return PLAYLIST_RESULT_DISABLED;
-
- if (!spl_valid_name(name_utf8))
- return PLAYLIST_RESULT_BAD_NAME;
-
- path_fs = map_spl_utf8_to_fs(name_utf8);
+ path_fs = spl_map_to_fs(name_utf8, error_r);
if (path_fs == NULL)
- return PLAYLIST_RESULT_BAD_NAME;
+ return false;
ret = unlink(path_fs);
g_free(path_fs);
- if (ret < 0)
- return errno == ENOENT
- ? PLAYLIST_RESULT_NO_SUCH_LIST
- : PLAYLIST_RESULT_ERRNO;
+ if (ret < 0) {
+ playlist_errno(error_r);
+ return false;
+ }
idle_add(IDLE_STORED_PLAYLIST);
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
}
-enum playlist_result
-spl_remove_index(const char *utf8path, unsigned pos)
+bool
+spl_remove_index(const char *utf8path, unsigned pos, GError **error_r)
{
- GPtrArray *list;
char *uri;
- enum playlist_result result;
- if (!(list = spl_load(utf8path)))
- return PLAYLIST_RESULT_NO_SUCH_LIST;
+ GPtrArray *list = spl_load(utf8path, error_r);
+ if (list == NULL)
+ return false;
if (pos >= list->len) {
spl_free(list);
- return PLAYLIST_RESULT_BAD_RANGE;
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_BAD_RANGE,
+ "Bad range");
+ return false;
}
uri = spl_remove_index_internal(list, pos);
g_free(uri);
- result = spl_save(list, utf8path);
+ bool result = spl_save(list, utf8path, error_r);
spl_free(list);
@@ -376,38 +435,38 @@ spl_remove_index(const char *utf8path, unsigned pos)
return result;
}
-enum playlist_result
-spl_append_song(const char *utf8path, struct song *song)
+bool
+spl_append_song(const char *utf8path, struct song *song, GError **error_r)
{
FILE *file;
struct stat st;
- char *path_fs;
-
- if (map_spl_path() == NULL)
- return PLAYLIST_RESULT_DISABLED;
- if (!spl_valid_name(utf8path))
- return PLAYLIST_RESULT_BAD_NAME;
+ if (spl_map(error_r) == NULL)
+ return false;
- path_fs = map_spl_utf8_to_fs(utf8path);
+ char *path_fs = spl_map_to_fs(utf8path, error_r);
if (path_fs == NULL)
- return PLAYLIST_RESULT_BAD_NAME;
+ return false;
file = fopen(path_fs, "a");
g_free(path_fs);
- if (file == NULL)
- return PLAYLIST_RESULT_ERRNO;
+ if (file == NULL) {
+ playlist_errno(error_r);
+ return false;
+ }
if (fstat(fileno(file), &st) < 0) {
- int save_errno = errno;
+ playlist_errno(error_r);
fclose(file);
- errno = save_errno;
- return PLAYLIST_RESULT_ERRNO;
+ return false;
}
if (st.st_size / (MPD_PATH_MAX + 1) >= (off_t)playlist_max_length) {
fclose(file);
- return PLAYLIST_RESULT_TOO_LARGE;
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_TOO_LARGE,
+ "Stored playlist is too large");
+ return false;
}
playlist_print_song(file, song);
@@ -415,68 +474,79 @@ spl_append_song(const char *utf8path, struct song *song)
fclose(file);
idle_add(IDLE_STORED_PLAYLIST);
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
}
-enum playlist_result
-spl_append_uri(const char *url, const char *utf8file)
+bool
+spl_append_uri(const char *url, const char *utf8file, GError **error_r)
{
struct song *song;
if (uri_has_scheme(url)) {
- enum playlist_result ret;
-
song = song_remote_new(url);
- ret = spl_append_song(utf8file, song);
+ bool success = spl_append_song(utf8file, song, error_r);
song_free(song);
- return ret;
+ return success;
} else {
song = db_get_song(url);
- if (song == NULL)
- return PLAYLIST_RESULT_NO_SUCH_SONG;
-
- return spl_append_song(utf8file, song);
+ if (song == NULL) {
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_NO_SUCH_SONG,
+ "No such song");
+ return false;
+ }
+
+ return spl_append_song(utf8file, song, error_r);
}
}
-static enum playlist_result
-spl_rename_internal(const char *from_path_fs, const char *to_path_fs)
+static bool
+spl_rename_internal(const char *from_path_fs, const char *to_path_fs,
+ GError **error_r)
{
- if (!g_file_test(from_path_fs, G_FILE_TEST_IS_REGULAR))
- return PLAYLIST_RESULT_NO_SUCH_LIST;
+ if (!g_file_test(from_path_fs, G_FILE_TEST_IS_REGULAR)) {
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_NO_SUCH_LIST,
+ "No such playlist");
+ return false;
+ }
- if (g_file_test(to_path_fs, G_FILE_TEST_EXISTS))
- return PLAYLIST_RESULT_LIST_EXISTS;
+ if (g_file_test(to_path_fs, G_FILE_TEST_EXISTS)) {
+ g_set_error_literal(error_r, playlist_quark(),
+ PLAYLIST_RESULT_LIST_EXISTS,
+ "Playlist exists already");
+ return false;
+ }
- if (rename(from_path_fs, to_path_fs) < 0)
- return PLAYLIST_RESULT_ERRNO;
+ if (rename(from_path_fs, to_path_fs) < 0) {
+ playlist_errno(error_r);
+ return false;
+ }
idle_add(IDLE_STORED_PLAYLIST);
- return PLAYLIST_RESULT_SUCCESS;
+ return true;
}
-enum playlist_result
-spl_rename(const char *utf8from, const char *utf8to)
+bool
+spl_rename(const char *utf8from, const char *utf8to, GError **error_r)
{
- char *from_path_fs, *to_path_fs;
- static enum playlist_result ret;
-
- if (map_spl_path() == NULL)
- return PLAYLIST_RESULT_DISABLED;
+ if (spl_map(error_r) == NULL)
+ return false;
- if (!spl_valid_name(utf8from) || !spl_valid_name(utf8to))
- return PLAYLIST_RESULT_BAD_NAME;
+ char *from_path_fs = spl_map_to_fs(utf8from, error_r);
+ if (from_path_fs == NULL)
+ return false;
- from_path_fs = map_spl_utf8_to_fs(utf8from);
- to_path_fs = map_spl_utf8_to_fs(utf8to);
+ char *to_path_fs = spl_map_to_fs(utf8to, error_r);
+ if (to_path_fs == NULL) {
+ g_free(from_path_fs);
+ return false;
+ }
- if (from_path_fs != NULL && to_path_fs != NULL)
- ret = spl_rename_internal(from_path_fs, to_path_fs);
- else
- ret = PLAYLIST_RESULT_BAD_NAME;
+ bool success = spl_rename_internal(from_path_fs, to_path_fs, error_r);
g_free(from_path_fs);
g_free(to_path_fs);
- return ret;
+ return success;
}
diff --git a/src/stored_playlist.h b/src/stored_playlist.h
index 3afdbb0f0..cfe49633c 100644
--- a/src/stored_playlist.h
+++ b/src/stored_playlist.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,8 +20,6 @@
#ifndef MPD_STORED_PLAYLIST_H
#define MPD_STORED_PLAYLIST_H
-#include "playlist.h"
-
#include <glib.h>
#include <stdbool.h>
#include <time.h>
@@ -51,39 +49,40 @@ spl_valid_name(const char *name_utf8);
/**
* Returns a list of stored_playlist_info struct pointers. Returns
- * NULL if an error occured.
+ * NULL if an error occurred.
*/
GPtrArray *
-spl_list(void);
+spl_list(GError **error_r);
void
spl_list_free(GPtrArray *list);
GPtrArray *
-spl_load(const char *utf8path);
+spl_load(const char *utf8path, GError **error_r);
void
spl_free(GPtrArray *list);
-enum playlist_result
-spl_move_index(const char *utf8path, unsigned src, unsigned dest);
+bool
+spl_move_index(const char *utf8path, unsigned src, unsigned dest,
+ GError **error_r);
-enum playlist_result
-spl_clear(const char *utf8path);
+bool
+spl_clear(const char *utf8path, GError **error_r);
-enum playlist_result
-spl_delete(const char *name_utf8);
+bool
+spl_delete(const char *name_utf8, GError **error_r);
-enum playlist_result
-spl_remove_index(const char *utf8path, unsigned pos);
+bool
+spl_remove_index(const char *utf8path, unsigned pos, GError **error_r);
-enum playlist_result
-spl_append_song(const char *utf8path, struct song *song);
+bool
+spl_append_song(const char *utf8path, struct song *song, GError **error_r);
-enum playlist_result
-spl_append_uri(const char *file, const char *utf8file);
+bool
+spl_append_uri(const char *file, const char *utf8file, GError **error_r);
-enum playlist_result
-spl_rename(const char *utf8from, const char *utf8to);
+bool
+spl_rename(const char *utf8from, const char *utf8to, GError **error_r);
#endif
diff --git a/src/string_util.c b/src/string_util.c
new file mode 100644
index 000000000..6e5429076
--- /dev/null
+++ b/src/string_util.c
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "string_util.h"
+
+#include <glib.h>
+
+#include <assert.h>
+
+const char *
+strchug_fast_c(const char *p)
+{
+ while (*p != 0 && g_ascii_isspace(*p))
+ ++p;
+
+ return p;
+}
+
+bool
+string_array_contains(const char *const* haystack, const char *needle)
+{
+ assert(haystack != NULL);
+ assert(needle != NULL);
+
+ for (; *haystack != NULL; ++haystack)
+ if (g_ascii_strcasecmp(*haystack, needle) == 0)
+ return true;
+
+ return false;
+}
diff --git a/src/string_util.h b/src/string_util.h
new file mode 100644
index 000000000..dc80a46ef
--- /dev/null
+++ b/src/string_util.h
@@ -0,0 +1,77 @@
+/*
+ * 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_STRING_UTIL_H
+#define MPD_STRING_UTIL_H
+
+#include <glib.h>
+
+#include <stdbool.h>
+
+/**
+ * Remove the "const" attribute from a string pointer. This is a
+ * dirty hack, don't use it unless you know what you're doing!
+ */
+G_GNUC_CONST
+static inline char *
+deconst_string(const char *p)
+{
+ union {
+ const char *in;
+ char *out;
+ } u = {
+ .in = p,
+ };
+
+ return u.out;
+}
+
+/**
+ * Returns a pointer to the first non-whitespace character in the
+ * string, or to the end of the string.
+ *
+ * This is a faster version of g_strchug(), because it does not move
+ * data.
+ */
+G_GNUC_PURE
+const char *
+strchug_fast_c(const char *p);
+
+/**
+ * Same as strchug_fast_c(), but works with a writable pointer.
+ */
+G_GNUC_PURE
+static inline char *
+strchug_fast(char *p)
+{
+ return deconst_string(strchug_fast_c(p));
+}
+
+/**
+ * Checks whether a string array contains the specified string.
+ *
+ * @param haystack a NULL terminated list of strings
+ * @param needle the string to search for; the comparison is
+ * case-insensitive for ASCII characters
+ * @return true if found
+ */
+bool
+string_array_contains(const char *const* haystack, const char *needle);
+
+#endif
diff --git a/src/strset.c b/src/strset.c
index e071fbc98..5862e4075 100644
--- a/src/strset.c
+++ b/src/strset.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/strset.h b/src/strset.h
index 9a7aa45e5..5382e59b8 100644
--- a/src/strset.h
+++ b/src/strset.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag.c b/src/tag.c
index 6ce46a831..1dffe9c26 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag.h b/src/tag.h
index 6931453f7..530f84c4b 100644
--- a/src/tag.h
+++ b/src/tag.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -73,7 +73,7 @@ struct tag_item {
* the value of this tag; this is a variable length string
*/
char value[sizeof(long)];
-} mpd_packed;
+} gcc_packed;
/**
* The meta information about a song file. It is a MPD specific
diff --git a/src/tag_ape.c b/src/tag_ape.c
index 79facba1b..1978ea39b 100644
--- a/src/tag_ape.c
+++ b/src/tag_ape.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_ape.h b/src/tag_ape.h
index 150659685..eb0f1b8a5 100644
--- a/src/tag_ape.h
+++ b/src/tag_ape.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_id3.c b/src/tag_id3.c
index 9c0a98d40..829b196b8 100644
--- a/src/tag_id3.c
+++ b/src/tag_id3.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_id3.h b/src/tag_id3.h
index 43f9678b4..17dde4b36 100644
--- a/src/tag_id3.h
+++ b/src/tag_id3.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_internal.h b/src/tag_internal.h
index 9d76efed1..af05cc6b6 100644
--- a/src/tag_internal.h
+++ b/src/tag_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,8 @@
#ifndef MPD_TAG_INTERNAL_H
#define MPD_TAG_INTERNAL_H
+#include "tag.h"
+
#include <stdbool.h>
extern bool ignore_tag_items[TAG_NUM_OF_ITEM_TYPES];
diff --git a/src/tag_pool.c b/src/tag_pool.c
index 6ad1e1f2d..eabf3e369 100644
--- a/src/tag_pool.c
+++ b/src/tag_pool.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_pool.h b/src/tag_pool.h
index 289d6fe5f..a96c00d85 100644
--- a/src/tag_pool.h
+++ b/src/tag_pool.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_print.c b/src/tag_print.c
index 493fa89b5..9a46b247a 100644
--- a/src/tag_print.c
+++ b/src/tag_print.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_print.h b/src/tag_print.h
index e16e2c441..b9eeeaecf 100644
--- a/src/tag_print.h
+++ b/src/tag_print.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_rva2.c b/src/tag_rva2.c
index 35f12118f..68ae9d5e5 100644
--- a/src/tag_rva2.c
+++ b/src/tag_rva2.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_rva2.h b/src/tag_rva2.h
index a92c97912..8aac2fe9f 100644
--- a/src/tag_rva2.h
+++ b/src/tag_rva2.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_save.c b/src/tag_save.c
index 9b90d1b92..efc476e19 100644
--- a/src/tag_save.c
+++ b/src/tag_save.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_save.h b/src/tag_save.h
index 2e8924c20..9f6a580c8 100644
--- a/src/tag_save.h
+++ b/src/tag_save.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tag_table.h b/src/tag_table.h
index ce47d69fc..367a3de5f 100644
--- a/src/tag_table.h
+++ b/src/tag_table.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/tcp_socket.c b/src/tcp_socket.c
new file mode 100644
index 000000000..f65b9c07c
--- /dev/null
+++ b/src/tcp_socket.c
@@ -0,0 +1,381 @@
+/*
+ * 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.
+ */
+
+#include "tcp_socket.h"
+#include "fifo_buffer.h"
+#include "io_thread.h"
+
+#include <assert.h>
+#include <string.h>
+
+#ifdef WIN32
+#define WINVER 0x0501
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+struct tcp_socket {
+ const struct tcp_socket_handler *handler;
+ void *handler_ctx;
+
+ GMutex *mutex;
+
+ GIOChannel *channel;
+ GSource *in_source, *out_source;
+
+ struct fifo_buffer *input, *output;
+};
+
+static gboolean
+tcp_event(GIOChannel *source, GIOCondition condition, gpointer data);
+
+static void
+tcp_socket_schedule_read(struct tcp_socket *s)
+{
+ assert(s->input != NULL);
+ assert(!fifo_buffer_is_full(s->input));
+
+ if (s->in_source != NULL)
+ return;
+
+ s->in_source = g_io_create_watch(s->channel,
+ G_IO_IN|G_IO_ERR|G_IO_HUP);
+ g_source_set_callback(s->in_source, (GSourceFunc)tcp_event, s, NULL);
+ g_source_attach(s->in_source, io_thread_context());
+}
+
+static void
+tcp_socket_unschedule_read(struct tcp_socket *s)
+{
+ if (s->in_source == NULL)
+ return;
+
+ g_source_destroy(s->in_source);
+ g_source_unref(s->in_source);
+ s->in_source = NULL;
+}
+
+static void
+tcp_socket_schedule_write(struct tcp_socket *s)
+{
+ assert(s->output != NULL);
+ assert(!fifo_buffer_is_empty(s->output));
+
+ if (s->out_source != NULL)
+ return;
+
+ s->out_source = g_io_create_watch(s->channel, G_IO_OUT);
+ g_source_set_callback(s->out_source, (GSourceFunc)tcp_event, s, NULL);
+ g_source_attach(s->out_source, io_thread_context());
+}
+
+static void
+tcp_socket_unschedule_write(struct tcp_socket *s)
+{
+ if (s->out_source == NULL)
+ return;
+
+ g_source_destroy(s->out_source);
+ g_source_unref(s->out_source);
+ s->out_source = NULL;
+}
+
+/**
+ * Close the socket. Caller must lock the mutex.
+ */
+static void
+tcp_socket_close(struct tcp_socket *s)
+{
+ tcp_socket_unschedule_read(s);
+ tcp_socket_unschedule_write(s);
+
+ if (s->channel != NULL) {
+ g_io_channel_unref(s->channel);
+ s->channel = NULL;
+ }
+
+ if (s->input != NULL) {
+ fifo_buffer_free(s->input);
+ s->input = NULL;
+ }
+
+ if (s->output != NULL) {
+ fifo_buffer_free(s->output);
+ s->output = NULL;
+ }
+}
+
+static gpointer
+tcp_socket_close_callback(gpointer data)
+{
+ struct tcp_socket *s = data;
+
+ g_mutex_lock(s->mutex);
+ tcp_socket_close(s);
+ g_mutex_unlock(s->mutex);
+
+ return NULL;
+}
+
+static void
+tcp_socket_close_indirect(struct tcp_socket *s)
+{
+ io_thread_call(tcp_socket_close_callback, s);
+
+ assert(s->channel == NULL);
+ assert(s->in_source == NULL);
+ assert(s->out_source == NULL);
+}
+
+static void
+tcp_handle_input(struct tcp_socket *s)
+{
+ size_t length;
+ const void *p = fifo_buffer_read(s->input, &length);
+ if (p == NULL)
+ return;
+
+ g_mutex_unlock(s->mutex);
+ size_t consumed = s->handler->data(p, length, s->handler_ctx);
+ g_mutex_lock(s->mutex);
+ if (consumed > 0 && s->input != NULL)
+ fifo_buffer_consume(s->input, consumed);
+}
+
+static bool
+tcp_in_event(struct tcp_socket *s)
+{
+ assert(s != NULL);
+ assert(s->channel != NULL);
+
+ g_mutex_lock(s->mutex);
+
+ size_t max_length;
+ void *p = fifo_buffer_write(s->input, &max_length);
+ if (p == NULL) {
+ GError *error = g_error_new_literal(tcp_socket_quark(), 0,
+ "buffer overflow");
+ tcp_socket_close(s);
+ g_mutex_unlock(s->mutex);
+ s->handler->error(error, s->handler_ctx);
+ return false;
+ }
+
+ gsize bytes_read;
+ GError *error = NULL;
+ GIOStatus status = g_io_channel_read_chars(s->channel,
+ p, max_length,
+ &bytes_read, &error);
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ fifo_buffer_append(s->input, bytes_read);
+ tcp_handle_input(s);
+ g_mutex_unlock(s->mutex);
+ return true;
+
+ case G_IO_STATUS_AGAIN:
+ /* try again later */
+ g_mutex_unlock(s->mutex);
+ return true;
+
+ case G_IO_STATUS_EOF:
+ /* peer disconnected */
+ tcp_socket_close(s);
+ g_mutex_unlock(s->mutex);
+ s->handler->disconnected(s->handler_ctx);
+ return false;
+
+ case G_IO_STATUS_ERROR:
+ /* I/O error */
+ tcp_socket_close(s);
+ g_mutex_unlock(s->mutex);
+ s->handler->error(error, s->handler_ctx);
+ return false;
+ }
+
+ /* unreachable */
+ assert(false);
+ return true;
+}
+
+static bool
+tcp_out_event(struct tcp_socket *s)
+{
+ assert(s != NULL);
+ assert(s->channel != NULL);
+
+ g_mutex_lock(s->mutex);
+
+ size_t length;
+ const void *p = fifo_buffer_read(s->output, &length);
+ if (p == NULL) {
+ /* no more data in the output buffer, remove the
+ output event */
+ tcp_socket_unschedule_write(s);
+ g_mutex_unlock(s->mutex);
+ return false;
+ }
+
+ gsize bytes_written;
+ GError *error = NULL;
+ GIOStatus status = g_io_channel_write_chars(s->channel, p, length,
+ &bytes_written, &error);
+ switch (status) {
+ case G_IO_STATUS_NORMAL:
+ fifo_buffer_consume(s->output, bytes_written);
+ g_mutex_unlock(s->mutex);
+ return true;
+
+ case G_IO_STATUS_AGAIN:
+ tcp_socket_schedule_write(s);
+ g_mutex_unlock(s->mutex);
+ return true;
+
+ case G_IO_STATUS_EOF:
+ /* peer disconnected */
+ tcp_socket_close(s);
+ g_mutex_unlock(s->mutex);
+ s->handler->disconnected(s->handler_ctx);
+ return false;
+
+ case G_IO_STATUS_ERROR:
+ /* I/O error */
+ tcp_socket_close(s);
+ g_mutex_unlock(s->mutex);
+ s->handler->error(error, s->handler_ctx);
+ return false;
+ }
+
+ /* unreachable */
+ g_mutex_unlock(s->mutex);
+ assert(false);
+ return true;
+}
+
+static gboolean
+tcp_event(G_GNUC_UNUSED GIOChannel *source, GIOCondition condition,
+ gpointer data)
+{
+ struct tcp_socket *s = data;
+
+ assert(source == s->channel);
+
+ switch (condition) {
+ case G_IO_IN:
+ case G_IO_PRI:
+ return tcp_in_event(s);
+
+ case G_IO_OUT:
+ return tcp_out_event(s);
+
+ case G_IO_ERR:
+ case G_IO_HUP:
+ case G_IO_NVAL:
+ tcp_socket_close(s);
+ s->handler->disconnected(s->handler_ctx);
+ return false;
+ }
+
+ /* unreachable */
+ assert(false);
+ return false;
+}
+
+struct tcp_socket *
+tcp_socket_new(int fd,
+ const struct tcp_socket_handler *handler, void *ctx)
+{
+ assert(fd >= 0);
+ assert(handler != NULL);
+ assert(handler->data != NULL);
+ assert(handler->error != NULL);
+ assert(handler->disconnected != NULL);
+
+ struct tcp_socket *s = g_new(struct tcp_socket, 1);
+ s->handler = handler;
+ s->handler_ctx = ctx;
+ s->mutex = g_mutex_new();
+
+ g_mutex_lock(s->mutex);
+
+#ifndef G_OS_WIN32
+ s->channel = g_io_channel_unix_new(fd);
+#else
+ s->channel = g_io_channel_win32_new_socket(fd);
+#endif
+ /* GLib is responsible for closing the file descriptor */
+ g_io_channel_set_close_on_unref(s->channel, true);
+ /* NULL encoding means the stream is binary safe */
+ g_io_channel_set_encoding(s->channel, NULL, NULL);
+ /* no buffering */
+ g_io_channel_set_buffered(s->channel, false);
+
+ s->input = fifo_buffer_new(4096);
+ s->output = fifo_buffer_new(4096);
+
+ s->in_source = NULL;
+ s->out_source = NULL;
+
+ tcp_socket_schedule_read(s);
+
+ g_mutex_unlock(s->mutex);
+
+ return s;
+}
+
+void
+tcp_socket_free(struct tcp_socket *s)
+{
+ tcp_socket_close_indirect(s);
+ g_mutex_free(s->mutex);
+ g_free(s);
+}
+
+bool
+tcp_socket_send(struct tcp_socket *s, const void *data, size_t length)
+{
+ assert(s != NULL);
+
+ g_mutex_lock(s->mutex);
+
+ if (s->output == NULL || s->channel == NULL) {
+ /* already disconnected */
+ g_mutex_unlock(s->mutex);
+ return false;
+ }
+
+ size_t max_length;
+ void *p = fifo_buffer_write(s->output, &max_length);
+ if (p == NULL || max_length < length) {
+ /* buffer is full */
+ g_mutex_unlock(s->mutex);
+ return false;
+ }
+
+ memcpy(p, data, length);
+ fifo_buffer_append(s->output, length);
+ tcp_socket_schedule_write(s);
+
+ g_mutex_unlock(s->mutex);
+ return true;
+}
+
diff --git a/src/tcp_socket.h b/src/tcp_socket.h
new file mode 100644
index 000000000..b6b367b86
--- /dev/null
+++ b/src/tcp_socket.h
@@ -0,0 +1,61 @@
+/*
+ * 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_TCP_SOCKET_H
+#define MPD_TCP_SOCKET_H
+
+#include <glib.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+struct sockaddr;
+
+struct tcp_socket_handler {
+ /**
+ * New data has arrived.
+ *
+ * @return the number of bytes consumed; 0 if more data is
+ * needed
+ */
+ size_t (*data)(const void *data, size_t length, void *ctx);
+
+ void (*error)(GError *error, void *ctx);
+
+ void (*disconnected)(void *ctx);
+};
+
+static inline GQuark
+tcp_socket_quark(void)
+{
+ return g_quark_from_static_string("tcp_socket");
+}
+
+G_GNUC_MALLOC
+struct tcp_socket *
+tcp_socket_new(int fd,
+ const struct tcp_socket_handler *handler, void *ctx);
+
+void
+tcp_socket_free(struct tcp_socket *s);
+
+bool
+tcp_socket_send(struct tcp_socket *s, const void *data, size_t length);
+
+#endif
diff --git a/src/text_file.c b/src/text_file.c
index 355217aba..3674e5ce2 100644
--- a/src/text_file.c
+++ b/src/text_file.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/text_file.h b/src/text_file.h
index d016f8f7a..9dd810943 100644
--- a/src/text_file.h
+++ b/src/text_file.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/text_input_stream.c b/src/text_input_stream.c
index 29fb6dce6..c71e113c7 100644
--- a/src/text_input_stream.c
+++ b/src/text_input_stream.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/text_input_stream.h b/src/text_input_stream.h
index a1fda065d..9b3245689 100644
--- a/src/text_input_stream.h
+++ b/src/text_input_stream.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/timer.c b/src/timer.c
index 0b3b1198a..bbc7a4ab9 100644
--- a/src/timer.c
+++ b/src/timer.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -37,9 +37,9 @@ static uint64_t now(void)
return ((uint64_t)tv.tv_sec * 1000000) + tv.tv_usec;
}
-Timer *timer_new(const struct audio_format *af)
+struct timer *timer_new(const struct audio_format *af)
{
- Timer *timer = g_new(Timer, 1);
+ struct timer *timer = g_new(struct timer, 1);
timer->time = 0;
timer->started = 0;
timer->rate = af->sample_rate * audio_format_frame_size(af);
@@ -47,24 +47,24 @@ Timer *timer_new(const struct audio_format *af)
return timer;
}
-void timer_free(Timer *timer)
+void timer_free(struct timer *timer)
{
g_free(timer);
}
-void timer_start(Timer *timer)
+void timer_start(struct timer *timer)
{
timer->time = now();
timer->started = 1;
}
-void timer_reset(Timer *timer)
+void timer_reset(struct timer *timer)
{
timer->time = 0;
timer->started = 0;
}
-void timer_add(Timer *timer, int size)
+void timer_add(struct timer *timer, int size)
{
assert(timer->started);
@@ -72,7 +72,7 @@ void timer_add(Timer *timer, int size)
}
unsigned
-timer_delay(const Timer *timer)
+timer_delay(const struct timer *timer)
{
int64_t delay = (int64_t)(timer->time - now()) / 1000;
if (delay < 0)
@@ -84,7 +84,7 @@ timer_delay(const Timer *timer)
return delay / 1000;
}
-void timer_sync(Timer *timer)
+void timer_sync(struct timer *timer)
{
int64_t sleep_duration;
diff --git a/src/timer.h b/src/timer.h
index bbd895b31..184881249 100644
--- a/src/timer.h
+++ b/src/timer.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -24,28 +24,28 @@
struct audio_format;
-typedef struct _Timer {
+struct timer {
uint64_t time;
int started;
int rate;
-} Timer;
+};
-Timer *timer_new(const struct audio_format *af);
+struct timer *timer_new(const struct audio_format *af);
-void timer_free(Timer *timer);
+void timer_free(struct timer *timer);
-void timer_start(Timer *timer);
+void timer_start(struct timer *timer);
-void timer_reset(Timer *timer);
+void timer_reset(struct timer *timer);
-void timer_add(Timer *timer, int size);
+void timer_add(struct timer *timer, int size);
/**
* Returns the number of milliseconds to sleep to get back to sync.
*/
unsigned
-timer_delay(const Timer *timer);
+timer_delay(const struct timer *timer);
-void timer_sync(Timer *timer);
+void timer_sync(struct timer *timer);
#endif
diff --git a/src/tokenizer.c b/src/tokenizer.c
index 2b9e05070..bbb34e100 100644
--- a/src/tokenizer.c
+++ b/src/tokenizer.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "config.h"
#include "tokenizer.h"
+#include "string_util.h"
#include <stdbool.h>
#include <assert.h>
@@ -72,7 +73,7 @@ tokenizer_next_word(char **input_p, GError **error_r)
/* a whitespace: the word ends here */
*input = 0;
/* skip all following spaces, too */
- input = g_strchug(input + 1);
+ input = strchug_fast(input + 1);
break;
}
@@ -126,7 +127,7 @@ tokenizer_next_unquoted(char **input_p, GError **error_r)
/* a whitespace: the word ends here */
*input = 0;
/* skip all following spaces, too */
- input = g_strchug(input + 1);
+ input = strchug_fast(input + 1);
break;
}
@@ -205,7 +206,7 @@ tokenizer_next_string(char **input_p, GError **error_r)
/* finish the string and return it */
*dest = 0;
- *input_p = g_strchug(input);
+ *input_p = strchug_fast(input);
return word;
}
diff --git a/src/tokenizer.h b/src/tokenizer.h
index 61ff398a4..d55eb3ca6 100644
--- a/src/tokenizer.h
+++ b/src/tokenizer.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/udp_server.c b/src/udp_server.c
new file mode 100644
index 000000000..152eb37d1
--- /dev/null
+++ b/src/udp_server.c
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#include "udp_server.h"
+#include "io_thread.h"
+#include "gcc.h"
+
+#include <glib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#ifdef WIN32
+#define WINVER 0x0501
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#endif
+
+#if GCC_CHECK_VERSION(4, 2)
+/* allow C99 initialisers on struct sockaddr_in, even if the
+ (non-portable) attribute "sin_zero" is missing */
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+struct udp_server {
+ const struct udp_server_handler *handler;
+ void *handler_ctx;
+
+ int fd;
+ GIOChannel *channel;
+ GSource *source;
+
+ char buffer[8192];
+};
+
+static gboolean
+udp_in_event(G_GNUC_UNUSED GIOChannel *source,
+ G_GNUC_UNUSED GIOCondition condition,
+ gpointer data)
+{
+ struct udp_server *udp = data;
+
+ struct sockaddr_storage address_storage;
+ struct sockaddr *address = (struct sockaddr *)&address_storage;
+ socklen_t address_length = sizeof(address_storage);
+
+ ssize_t nbytes = recvfrom(udp->fd, udp->buffer, sizeof(udp->buffer),
+#ifdef WIN32
+ 0,
+#else
+ MSG_DONTWAIT,
+#endif
+ address, &address_length);
+ if (nbytes <= 0)
+ return true;
+
+ udp->handler->datagram(udp->fd, udp->buffer, nbytes,
+ address, address_length, udp->handler_ctx);
+ return true;
+}
+
+struct udp_server *
+udp_server_new(unsigned port,
+ const struct udp_server_handler *handler, void *ctx,
+ GError **error_r)
+{
+ int fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ g_set_error(error_r, udp_server_quark(), errno,
+ "failed to create UDP socket: %s",
+ g_strerror(errno));
+ return NULL;
+ }
+
+ const struct sockaddr_in address = {
+ .sin_family = AF_INET,
+ .sin_addr = {
+ .s_addr = htonl(INADDR_ANY),
+ },
+ .sin_port = htons(port),
+ };
+
+ if (bind(fd, (const struct sockaddr *)&address, sizeof(address)) < 0) {
+ g_set_error(error_r, udp_server_quark(), errno,
+ "failed to bind UDP port %u: %s",
+ port, g_strerror(errno));
+ close(fd);
+ return NULL;
+ }
+
+ struct udp_server *udp = g_new(struct udp_server, 1);
+ udp->handler = handler;
+ udp->handler_ctx = ctx;
+
+ udp->fd = fd;
+#ifndef G_OS_WIN32
+ udp->channel = g_io_channel_unix_new(fd);
+#else
+ udp->channel = g_io_channel_win32_new_socket(fd);
+#endif
+ /* NULL encoding means the stream is binary safe */
+ g_io_channel_set_encoding(udp->channel, NULL, NULL);
+ /* no buffering */
+ g_io_channel_set_buffered(udp->channel, false);
+
+ udp->source = g_io_create_watch(udp->channel, G_IO_IN);
+ g_source_set_callback(udp->source, (GSourceFunc)udp_in_event, udp,
+ NULL);
+ g_source_attach(udp->source, io_thread_context());
+
+ return udp;
+}
+
+void
+udp_server_free(struct udp_server *udp)
+{
+ g_source_destroy(udp->source);
+ g_source_unref(udp->source);
+ g_io_channel_unref(udp->channel);
+ close(udp->fd);
+ g_free(udp);
+}
diff --git a/src/udp_server.h b/src/udp_server.h
new file mode 100644
index 000000000..9e3471a45
--- /dev/null
+++ b/src/udp_server.h
@@ -0,0 +1,52 @@
+/*
+ * 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_UDP_SERVER_H
+#define MPD_UDP_SERVER_H
+
+#include <glib.h>
+
+#include <stddef.h>
+
+struct sockaddr;
+
+struct udp_server_handler {
+ /**
+ * A datagram was received.
+ */
+ void (*datagram)(int fd, const void *data, size_t length,
+ const struct sockaddr *source_address,
+ size_t source_address_length, void *ctx);
+};
+
+static inline GQuark
+udp_server_quark(void)
+{
+ return g_quark_from_static_string("udp_server");
+}
+
+struct udp_server *
+udp_server_new(unsigned port,
+ const struct udp_server_handler *handler, void *ctx,
+ GError **error_r);
+
+void
+udp_server_free(struct udp_server *udp);
+
+#endif
diff --git a/src/update.c b/src/update.c
index d57fb114d..1a3060653 100644
--- a/src/update.c
+++ b/src/update.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -68,8 +68,14 @@ static void * update_task(void *_path)
modified = update_walk(path, discard);
- if (modified || !db_exists())
- db_save();
+ if (modified || !db_exists()) {
+ GError *error = NULL;
+ if (!db_save(&error)) {
+ g_warning("Failed to save database: %s",
+ error->message);
+ g_error_free(error);
+ }
+ }
if (path != NULL && *path != 0)
g_debug("finished: %s", path);
diff --git a/src/update.h b/src/update.h
index 3f8a6f6a4..3d586b694 100644
--- a/src/update.h
+++ b/src/update.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/update_internal.h b/src/update_internal.h
index 65744f0d6..d4fe91014 100644
--- a/src/update_internal.h
+++ b/src/update_internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/update_queue.c b/src/update_queue.c
index d7b2d4e5f..4de250cc2 100644
--- a/src/update_queue.c
+++ b/src/update_queue.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/update_remove.c b/src/update_remove.c
index f7c2342a2..ca5fbd182 100644
--- a/src/update_remove.c
+++ b/src/update_remove.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,10 +19,10 @@
#include "config.h" /* must be first for large file support */
#include "update_internal.h"
-#include "notify.h"
#include "event_pipe.h"
#include "song.h"
#include "playlist.h"
+#include "main.h"
#ifdef ENABLE_SQLITE
#include "sticker.h"
@@ -35,7 +35,8 @@
static const struct song *removed_song;
-static struct notify remove_notify;
+static GMutex *remove_mutex;
+static GCond *remove_cond;
/**
* Safely remove a song from the database. This must be done in the
@@ -58,16 +59,20 @@ song_remove_event(void)
sticker_song_delete(removed_song);
#endif
- playlist_delete_song(&g_playlist, removed_song);
- removed_song = NULL;
+ playlist_delete_song(&g_playlist, global_player_control, removed_song);
- notify_signal(&remove_notify);
+ /* clear "removed_song" and send signal to update thread */
+ g_mutex_lock(remove_mutex);
+ removed_song = NULL;
+ g_cond_signal(remove_cond);
+ g_mutex_unlock(remove_mutex);
}
void
update_remove_global_init(void)
{
- notify_init(&remove_notify);
+ remove_mutex = g_mutex_new();
+ remove_cond = g_cond_new();
event_pipe_register(PIPE_EVENT_DELETE, song_remove_event);
}
@@ -75,7 +80,8 @@ update_remove_global_init(void)
void
update_remove_global_finish(void)
{
- notify_deinit(&remove_notify);
+ g_mutex_free(remove_mutex);
+ g_cond_free(remove_cond);
}
void
@@ -87,8 +93,10 @@ update_remove_song(const struct song *song)
event_pipe_emit(PIPE_EVENT_DELETE);
- do {
- notify_wait(&remove_notify);
- } while (removed_song != NULL);
+ g_mutex_lock(remove_mutex);
+
+ while (removed_song != NULL)
+ g_cond_wait(remove_cond, remove_mutex);
+ g_mutex_unlock(remove_mutex);
}
diff --git a/src/update_walk.c b/src/update_walk.c
index bf3c8f54b..e70ead173 100644
--- a/src/update_walk.c
+++ b/src/update_walk.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/uri.c b/src/uri.c
index f4d590a60..21a849f85 100644
--- a/src/uri.c
+++ b/src/uri.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/uri.h b/src/uri.h
index 422b959b0..85eeebe2e 100644
--- a/src/uri.h
+++ b/src/uri.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/utils.c b/src/utils.c
index 53494cc5d..d3b21d369 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -19,6 +19,7 @@
#include "config.h"
#include "utils.h"
+#include "glib_compat.h"
#include "conf.h"
#include <glib.h>
@@ -41,14 +42,25 @@
#include <windows.h>
#endif
-char *parsePath(char *path)
+G_GNUC_CONST
+static inline GQuark
+parse_path_quark(void)
{
+ return g_quark_from_static_string("path");
+}
+
+char *
+parsePath(const char *path, G_GNUC_UNUSED GError **error_r)
+{
+ assert(path != NULL);
+ assert(error_r == NULL || *error_r == NULL);
+
#ifndef WIN32
if (!g_path_is_absolute(path) && path[0] != '~') {
- g_warning("\"%s\" is not an absolute path", path);
+ g_set_error(error_r, parse_path_quark(), 0,
+ "not an absolute path: %s", path);
return NULL;
} else if (path[0] == '~') {
- size_t pos = 1;
const char *home;
if (path[1] == '/' || path[1] == '\0') {
@@ -56,7 +68,8 @@ char *parsePath(char *path)
if (user != NULL) {
struct passwd *passwd = getpwnam(user);
if (!passwd) {
- g_warning("no such user %s", user);
+ g_set_error(error_r, parse_path_quark(), 0,
+ "no such user: %s", user);
return NULL;
}
@@ -64,36 +77,37 @@ char *parsePath(char *path)
} else {
home = g_get_home_dir();
if (home == NULL) {
- g_warning("problems getting home "
- "for current user");
+ g_set_error_literal(error_r, parse_path_quark(), 0,
+ "problems getting home "
+ "for current user");
return NULL;
}
}
+
+ ++path;
} else {
- bool foundSlash = false;
- struct passwd *passwd;
- char *c;
-
- for (c = path + 1; *c != '\0' && *c != '/'; c++);
- if (*c == '/') {
- foundSlash = true;
- *c = '\0';
- }
- pos = c - path;
+ ++path;
- passwd = getpwnam(path + 1);
+ const char *slash = strchr(path, '/');
+ char *user = slash != NULL
+ ? g_strndup(path, slash - path)
+ : g_strdup(path);
+
+ struct passwd *passwd = getpwnam(user);
if (!passwd) {
- g_warning("user \"%s\" not found", path + 1);
+ g_set_error(error_r, parse_path_quark(), 0,
+ "no such user: %s", user);
+ g_free(user);
return NULL;
}
- if (foundSlash)
- *c = '/';
+ g_free(user);
home = passwd->pw_dir;
+ path = slash;
}
- return g_strconcat(home, path + pos, NULL);
+ return g_strconcat(home, path, NULL);
} else {
#endif
return g_strdup(path);
@@ -101,16 +115,3 @@ char *parsePath(char *path)
}
#endif
}
-
-bool
-string_array_contains(const char *const* haystack, const char *needle)
-{
- assert(haystack != NULL);
- assert(needle != NULL);
-
- for (; *haystack != NULL; ++haystack)
- if (g_ascii_strcasecmp(*haystack, needle) == 0)
- return true;
-
- return false;
-}
diff --git a/src/utils.h b/src/utils.h
index 629056637..f8d6657f2 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
#ifndef MPD_UTILS_H
#define MPD_UTILS_H
+#include <glib.h>
#include <stdbool.h>
#ifndef assert_static
@@ -31,17 +32,7 @@
} while (0)
#endif /* !assert_static */
-char *parsePath(char *path);
-
-/**
- * Checks whether a string array contains the specified string.
- *
- * @param haystack a NULL terminated list of strings
- * @param needle the string to search for; the comparison is
- * case-insensitive for ASCII characters
- * @return true if found
- */
-bool
-string_array_contains(const char *const* haystack, const char *needle);
+char *
+parsePath(const char *path, GError **error_r);
#endif
diff --git a/src/volume.c b/src/volume.c
index d7b72dd56..819e6fbfa 100644
--- a/src/volume.c
+++ b/src/volume.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/volume.h b/src/volume.h
index db266fec9..b08899a84 100644
--- a/src/volume.h
+++ b/src/volume.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/zeroconf-avahi.c b/src/zeroconf-avahi.c
index 518a7a481..f2cc5359b 100644
--- a/src/zeroconf-avahi.c
+++ b/src/zeroconf-avahi.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/zeroconf-bonjour.c b/src/zeroconf-bonjour.c
index 84f777c50..0f216aade 100644
--- a/src/zeroconf-bonjour.c
+++ b/src/zeroconf-bonjour.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/zeroconf-internal.h b/src/zeroconf-internal.h
index 7cb962431..983e5c556 100644
--- a/src/zeroconf-internal.h
+++ b/src/zeroconf-internal.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/zeroconf.c b/src/zeroconf.c
index 7b00789b6..907eb8bfc 100644
--- a/src/zeroconf.c
+++ b/src/zeroconf.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/zeroconf.h b/src/zeroconf.h
index 23354f87d..8e33a3d89 100644
--- a/src/zeroconf.h
+++ b/src/zeroconf.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/dump_playlist.c b/test/dump_playlist.c
index a8cb4d750..bf3fed7c9 100644
--- a/test/dump_playlist.c
+++ b/test/dump_playlist.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
*/
#include "config.h"
+#include "io_thread.h"
#include "input_init.h"
#include "input_stream.h"
#include "tag_pool.h"
@@ -30,6 +31,7 @@
#include <glib.h>
#include <unistd.h>
+#include <stdlib.h>
static void
my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
@@ -73,6 +75,13 @@ int main(int argc, char **argv)
return 1;
}
+ io_thread_init();
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
if (!input_stream_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
@@ -150,6 +159,7 @@ int main(int argc, char **argv)
input_stream_close(is);
playlist_list_global_finish();
input_stream_global_finish();
+ io_thread_deinit();
config_global_finish();
tag_pool_deinit();
diff --git a/test/read_conf.c b/test/read_conf.c
index f1b38cafe..4f6005c6f 100644
--- a/test/read_conf.c
+++ b/test/read_conf.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/read_mixer.c b/test/read_mixer.c
index 1b5b093a3..b45a14400 100644
--- a/test/read_mixer.c
+++ b/test/read_mixer.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -55,6 +55,25 @@ pulse_output_set_volume(G_GNUC_UNUSED struct pulse_output *po,
#endif
+#ifdef ENABLE_RAOP_OUTPUT
+#include "output/raop_output_plugin.h"
+
+bool
+raop_set_volume(G_GNUC_UNUSED struct raop_data *rd,
+ G_GNUC_UNUSED unsigned volume,
+ G_GNUC_UNUSED GError **error_r)
+{
+ return false;
+}
+
+int
+raop_get_volume(G_GNUC_UNUSED struct raop_data *rd)
+{
+ return -1;
+}
+
+#endif
+
void
event_pipe_emit(G_GNUC_UNUSED enum pipe_event event)
{
diff --git a/test/read_tags.c b/test/read_tags.c
index 3e5e523bf..c2e3b2caa 100644
--- a/test/read_tags.c
+++ b/test/read_tags.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,12 +18,14 @@
*/
#include "config.h"
+#include "io_thread.h"
#include "decoder_list.h"
#include "decoder_api.h"
#include "input_init.h"
#include "input_stream.h"
#include "audio_format.h"
#include "pcm_volume.h"
+#include "tag_pool.h"
#include "tag_ape.h"
#include "tag_id3.h"
#include "idle.h"
@@ -32,6 +34,7 @@
#include <assert.h>
#include <unistd.h>
+#include <stdlib.h>
#ifdef HAVE_LOCALE_H
#include <locale.h>
@@ -164,6 +167,16 @@ int main(int argc, char **argv)
decoder_name = argv[1];
path = argv[2];
+ g_thread_init(NULL);
+ io_thread_init();
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ tag_pool_init();
+
if (!input_stream_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
@@ -195,6 +208,8 @@ int main(int argc, char **argv)
decoder_plugin_deinit_all();
input_stream_global_finish();
+ io_thread_deinit();
+
if (tag == NULL) {
g_printerr("Failed to read tags\n");
return 1;
@@ -215,5 +230,7 @@ int main(int argc, char **argv)
}
}
+ tag_pool_deinit();
+
return 0;
}
diff --git a/test/run_convert.c b/test/run_convert.c
index 415d7535c..57a3a2f7f 100644
--- a/test/run_convert.c
+++ b/test/run_convert.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/run_decoder.c b/test/run_decoder.c
index c997ebf8f..efc246f55 100644
--- a/test/run_decoder.c
+++ b/test/run_decoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
*/
#include "config.h"
+#include "io_thread.h"
#include "decoder_list.h"
#include "decoder_api.h"
#include "input_init.h"
@@ -31,6 +32,7 @@
#include <assert.h>
#include <unistd.h>
+#include <stdlib.h>
static void
my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
@@ -180,6 +182,13 @@ int main(int argc, char **argv)
g_log_set_default_handler(my_log_func, NULL);
+ io_thread_init();
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
if (!input_stream_global_init(&error)) {
g_warning("%s", error->message);
g_error_free(error);
@@ -222,6 +231,7 @@ int main(int argc, char **argv)
decoder_plugin_deinit_all();
input_stream_global_finish();
+ io_thread_deinit();
if (!decoder.initialized) {
g_printerr("Decoding failed\n");
diff --git a/test/run_encoder.c b/test/run_encoder.c
index 4b512d46a..5e6b158c6 100644
--- a/test/run_encoder.c
+++ b/test/run_encoder.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -75,7 +75,7 @@ int main(int argc, char **argv)
}
param = config_new_param(NULL, -1);
- config_add_block_param(param, "quality", "5.0", -1, NULL);
+ config_add_block_param(param, "quality", "5.0", -1);
encoder = encoder_init(plugin, param, &error);
if (encoder == NULL) {
diff --git a/test/run_filter.c b/test/run_filter.c
index 3758eb5bb..d1dffc190 100644
--- a/test/run_filter.c
+++ b/test/run_filter.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/run_inotify.c b/test/run_inotify.c
index 9f3c30b8c..3e7c70dba 100644
--- a/test/run_inotify.c
+++ b/test/run_inotify.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/run_input.c b/test/run_input.c
index a50cd70ab..c00698dff 100644
--- a/test/run_input.c
+++ b/test/run_input.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
*/
#include "config.h"
+#include "io_thread.h"
#include "input_init.h"
#include "input_stream.h"
#include "tag_pool.h"
@@ -32,6 +33,7 @@
#include <glib.h>
#include <unistd.h>
+#include <stdlib.h>
static void
my_log_func(const gchar *log_domain, G_GNUC_UNUSED GLogLevelFlags log_level,
@@ -122,6 +124,13 @@ int main(int argc, char **argv)
tag_pool_init();
config_global_init();
+ io_thread_init();
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
#ifdef ENABLE_ARCHIVE
archive_plugin_init_all();
#endif
@@ -155,6 +164,8 @@ int main(int argc, char **argv)
archive_plugin_deinit_all();
#endif
+ io_thread_deinit();
+
config_global_finish();
tag_pool_deinit();
diff --git a/test/run_normalize.c b/test/run_normalize.c
index dd1140782..d16ed60ea 100644
--- a/test/run_normalize.c
+++ b/test/run_normalize.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/run_ntp_server.c b/test/run_ntp_server.c
new file mode 100644
index 000000000..6f732a074
--- /dev/null
+++ b/test/run_ntp_server.c
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "ntp_server.h"
+#include "signals.h"
+#include "io_thread.h"
+
+#include <glib.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#ifdef WIN32
+#define WINVER 0x0501
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+
+void
+on_quit(void)
+{
+ io_thread_quit();
+}
+
+int
+main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
+{
+ g_thread_init(NULL);
+ signals_init();
+ io_thread_init();
+
+ struct ntp_server ntp;
+ ntp_server_init(&ntp);
+
+ GError *error = NULL;
+ if (!ntp_server_open(&ntp, &error)) {
+ io_thread_deinit();
+ g_printerr("%s\n", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
+ io_thread_run();
+
+ ntp_server_close(&ntp);
+ io_thread_deinit();
+ return EXIT_SUCCESS;
+}
diff --git a/test/run_output.c b/test/run_output.c
index 5028068ff..c1d7a8120 100644
--- a/test/run_output.c
+++ b/test/run_output.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -18,6 +18,7 @@
*/
#include "config.h"
+#include "io_thread.h"
#include "output_plugin.h"
#include "output_internal.h"
#include "output_control.h"
@@ -28,6 +29,7 @@
#include "event_pipe.h"
#include "idle.h"
#include "playlist.h"
+#include "player_control.h"
#include "stdbin.h"
#include <glib.h>
@@ -35,6 +37,7 @@
#include <assert.h>
#include <string.h>
#include <unistd.h>
+#include <stdlib.h>
struct playlist g_playlist;
@@ -104,7 +107,9 @@ load_audio_output(struct audio_output *ao, const char *name)
return false;
}
- success = audio_output_init(ao, param, &error);
+ static struct player_control dummy_player_control;
+
+ success = audio_output_init(ao, param, &dummy_player_control, &error);
if (!success) {
g_printerr("%s\n", error->message);
g_error_free(error);
@@ -113,16 +118,70 @@ load_audio_output(struct audio_output *ao, const char *name)
return success;
}
+static bool
+run_output(struct audio_output *ao, struct audio_format *audio_format)
+{
+ /* open the audio output */
+
+ GError *error = NULL;
+ if (!ao_plugin_open(ao->plugin, ao->data, audio_format, &error)) {
+ g_printerr("Failed to open audio output: %s\n",
+ error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ struct audio_format_string af_string;
+ g_printerr("audio_format=%s\n",
+ audio_format_to_string(audio_format, &af_string));
+
+ size_t frame_size = audio_format_frame_size(audio_format);
+
+ /* play */
+
+ size_t length = 0;
+ char buffer[4096];
+ while (true) {
+ if (length < sizeof(buffer)) {
+ ssize_t nbytes = read(0, buffer + length,
+ sizeof(buffer) - length);
+ if (nbytes <= 0)
+ break;
+
+ length += (size_t)nbytes;
+ }
+
+ size_t play_length = (length / frame_size) * frame_size;
+ if (play_length > 0) {
+ size_t consumed = ao_plugin_play(ao->plugin, ao->data,
+ buffer, play_length,
+ &error);
+ if (consumed == 0) {
+ ao_plugin_close(ao->plugin, ao->data);
+ g_printerr("Failed to play: %s\n",
+ error->message);
+ g_error_free(error);
+ return false;
+ }
+
+ assert(consumed <= length);
+ assert(consumed % frame_size == 0);
+
+ length -= consumed;
+ memmove(buffer, buffer + consumed, length);
+ }
+ }
+
+ ao_plugin_close(ao->plugin, ao->data);
+ return true;
+}
+
int main(int argc, char **argv)
{
struct audio_output ao;
struct audio_format audio_format;
- struct audio_format_string af_string;
bool success;
GError *error = NULL;
- char buffer[4096];
- ssize_t nbytes;
- size_t frame_size, length = 0, play_length, consumed;
if (argc < 3 || argc > 4) {
g_printerr("Usage: run_output CONFIG NAME [FORMAT] <IN\n");
@@ -143,6 +202,13 @@ int main(int argc, char **argv)
return 1;
}
+ io_thread_init();
+ if (!io_thread_start(&error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return EXIT_FAILURE;
+ }
+
/* initialize the audio output */
if (!load_audio_output(&ao, argv[2]))
@@ -161,59 +227,17 @@ int main(int argc, char **argv)
}
}
- /* open the audio output */
-
- success = ao_plugin_open(ao.plugin, ao.data, &audio_format, &error);
- if (!success) {
- g_printerr("Failed to open audio output: %s\n",
- error->message);
- g_error_free(error);
- return 1;
- }
-
- g_printerr("audio_format=%s\n",
- audio_format_to_string(&audio_format, &af_string));
-
- frame_size = audio_format_frame_size(&audio_format);
-
- /* play */
-
- while (true) {
- if (length < sizeof(buffer)) {
- nbytes = read(0, buffer + length, sizeof(buffer) - length);
- if (nbytes <= 0)
- break;
-
- length += (size_t)nbytes;
- }
+ /* do it */
- play_length = (length / frame_size) * frame_size;
- if (play_length > 0) {
- consumed = ao_plugin_play(ao.plugin, ao.data,
- buffer, play_length,
- &error);
- if (consumed == 0) {
- g_printerr("Failed to play: %s\n",
- error->message);
- g_error_free(error);
- return 1;
- }
-
- assert(consumed <= length);
- assert(consumed % frame_size == 0);
-
- length -= consumed;
- memmove(buffer, buffer + consumed, length);
- }
- }
+ success = run_output(&ao, &audio_format);
/* cleanup and exit */
- ao_plugin_close(ao.plugin, ao.data);
- ao_plugin_finish(ao.plugin, ao.data);
- g_mutex_free(ao.mutex);
+ audio_output_destruct(&ao);
+
+ io_thread_deinit();
config_global_finish();
- return 0;
+ return success ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/test/signals.c b/test/signals.c
new file mode 100644
index 000000000..5f5d336f3
--- /dev/null
+++ b/test/signals.c
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "signals.h"
+#ifndef WIN32
+
+#include "mpd_error.h"
+
+#include <glib.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+
+static void
+quit_signal_handler(G_GNUC_UNUSED int signum)
+{
+ on_quit();
+}
+
+static void
+x_sigaction(int signum, const struct sigaction *act)
+{
+ if (sigaction(signum, act, NULL) < 0)
+ MPD_ERROR("sigaction() failed: %s", strerror(errno));
+}
+
+#endif
+
+void
+signals_init(void)
+{
+#ifndef WIN32
+ struct sigaction sa;
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_IGN;
+ x_sigaction(SIGPIPE, &sa);
+
+ sa.sa_handler = quit_signal_handler;
+ x_sigaction(SIGINT, &sa);
+ x_sigaction(SIGTERM, &sa);
+#endif
+}
diff --git a/src/directory_print.h b/test/signals.h
index 0933f5a97..e524d35e2 100644
--- a/src/directory_print.h
+++ b/test/signals.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
@@ -17,13 +17,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifndef MPD_DIRECTORY_PRINT_H
-#define MPD_DIRECTORY_PRINT_H
+#ifndef MPD_SIGNALS_H
+#define MPD_SIGNALS_H
-struct client;
-struct directory;
+void
+on_quit(void);
void
-directory_print(struct client *client, const struct directory *directory);
+signals_init(void);
#endif
diff --git a/test/software_volume.c b/test/software_volume.c
index c4de69328..67dd1808e 100644
--- a/test/software_volume.c
+++ b/test/software_volume.c
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/stdbin.h b/test/stdbin.h
index 362605ad9..48cac7338 100644
--- a/test/stdbin.h
+++ b/test/stdbin.h
@@ -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
*
* This program is free software; you can redistribute it and/or modify
diff --git a/test/test_queue_priority.c b/test/test_queue_priority.c
new file mode 100644
index 000000000..d61b8c8da
--- /dev/null
+++ b/test/test_queue_priority.c
@@ -0,0 +1,174 @@
+#include "queue.h"
+#include "song.h"
+
+void
+song_free(G_GNUC_UNUSED struct song *song)
+{
+}
+
+G_GNUC_UNUSED
+static void
+dump_order(const struct queue *queue)
+{
+ g_printerr("queue length=%u, order:\n", queue_length(queue));
+ for (unsigned i = 0; i < queue_length(queue); ++i)
+ g_printerr(" [%u] -> %u (prio=%u)\n", i, queue->order[i],
+ queue->items[queue->order[i]].priority);
+}
+
+static void
+check_descending_priority(G_GNUC_UNUSED const struct queue *queue,
+ unsigned start_order)
+{
+ assert(start_order < queue_length(queue));
+
+ uint8_t last_priority = 0xff;
+ for (unsigned order = start_order; order < queue_length(queue); ++order) {
+ unsigned position = queue_order_to_position(queue, order);
+ uint8_t priority = queue->items[position].priority;
+ assert(priority <= last_priority);
+ last_priority = priority;
+ }
+}
+
+int
+main(G_GNUC_UNUSED int argc, G_GNUC_UNUSED char **argv)
+{
+ struct song songs[16];
+
+ struct queue queue;
+ queue_init(&queue, 32);
+
+ for (unsigned i = 0; i < G_N_ELEMENTS(songs); ++i)
+ queue_append(&queue, &songs[i]);
+
+ assert(queue_length(&queue) == G_N_ELEMENTS(songs));
+
+ /* priority=10 for 4 items */
+
+ queue_set_priority_range(&queue, 4, 8, 10, -1);
+
+ queue.random = true;
+ queue_shuffle_order(&queue);
+ check_descending_priority(&queue, 0);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ assert(queue_position_to_order(&queue, i) >= 4);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue_position_to_order(&queue, i) < 4);
+ }
+
+ for (unsigned i = 8; i < G_N_ELEMENTS(songs); ++i) {
+ assert(queue_position_to_order(&queue, i) >= 4);
+ }
+
+ /* priority=50 one more item */
+
+ queue_set_priority_range(&queue, 15, 16, 50, -1);
+ check_descending_priority(&queue, 0);
+
+ assert(queue_position_to_order(&queue, 15) == 0);
+
+ for (unsigned i = 0; i < 4; ++i) {
+ assert(queue_position_to_order(&queue, i) >= 4);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue_position_to_order(&queue, i) >= 1 &&
+ queue_position_to_order(&queue, i) < 5);
+ }
+
+ for (unsigned i = 8; i < 15; ++i) {
+ assert(queue_position_to_order(&queue, i) >= 5);
+ }
+
+ /* priority=20 for one of the 4 priority=10 items */
+
+ queue_set_priority_range(&queue, 3, 4, 20, -1);
+ check_descending_priority(&queue, 0);
+
+ assert(queue_position_to_order(&queue, 3) == 1);
+ assert(queue_position_to_order(&queue, 15) == 0);
+
+ for (unsigned i = 0; i < 3; ++i) {
+ assert(queue_position_to_order(&queue, i) >= 5);
+ }
+
+ for (unsigned i = 4; i < 8; ++i) {
+ assert(queue_position_to_order(&queue, i) >= 2 &&
+ queue_position_to_order(&queue, i) < 6);
+ }
+
+ for (unsigned i = 8; i < 15; ++i) {
+ assert(queue_position_to_order(&queue, i) >= 6);
+ }
+
+ /* priority=20 for another one of the 4 priority=10 items;
+ pass "after_order" (with priority=10) and see if it's moved
+ after that one */
+
+ unsigned current_order = 4;
+ unsigned current_position =
+ queue_order_to_position(&queue, current_order);
+
+ unsigned a_order = 3;
+ unsigned a_position = queue_order_to_position(&queue, a_order);
+ assert(queue.items[a_position].priority == 10);
+ queue_set_priority(&queue, a_position, 20, current_order);
+
+ current_order = queue_position_to_order(&queue, current_position);
+ assert(current_order == 3);
+
+ a_order = queue_position_to_order(&queue, a_position);
+ assert(a_order == 4);
+
+ check_descending_priority(&queue, current_order + 1);
+
+ /* priority=70 for one of the last items; must be inserted
+ right after the current song, before the priority=20 one we
+ just created */
+
+ unsigned b_order = 10;
+ unsigned b_position = queue_order_to_position(&queue, b_order);
+ assert(queue.items[b_position].priority == 0);
+ queue_set_priority(&queue, b_position, 70, current_order);
+
+ current_order = queue_position_to_order(&queue, current_position);
+ assert(current_order == 3);
+
+ b_order = queue_position_to_order(&queue, b_position);
+ assert(b_order == 4);
+
+ check_descending_priority(&queue, current_order + 1);
+
+ /* priority=60 for the old prio50 item; must not be moved,
+ because it's before the current song, and it's status
+ hasn't changed (it was already higher before) */
+
+ unsigned c_order = 0;
+ unsigned c_position = queue_order_to_position(&queue, c_order);
+ assert(queue.items[c_position].priority == 50);
+ queue_set_priority(&queue, c_position, 60, current_order);
+
+ current_order = queue_position_to_order(&queue, current_position);
+ assert(current_order == 3);
+
+ c_order = queue_position_to_order(&queue, c_position);
+ assert(c_order == 0);
+
+ /* move the prio=20 item back */
+
+ a_order = queue_position_to_order(&queue, a_position);
+ assert(a_order == 5);
+ assert(queue.items[a_position].priority == 20);
+ queue_set_priority(&queue, a_position, 5, current_order);
+
+
+ current_order = queue_position_to_order(&queue, current_position);
+ assert(current_order == 3);
+
+ a_order = queue_position_to_order(&queue, a_position);
+ assert(a_order == 6);
+}
diff --git a/valgrind.suppressions b/valgrind.suppressions
index 8d687f7b8..5538516a9 100644
--- a/valgrind.suppressions
+++ b/valgrind.suppressions
@@ -4,6 +4,14 @@
# bogus messages.
{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_random_int
+}
+
+{
g_main_context_dispatch
Memcheck:Leak
fun:malloc
@@ -90,108 +98,34 @@
}
{
- g_get_language_names
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strdup
- fun:g_get_language_names
-}
-
-{
- g_get_language_names
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strconcat
- fun:_g_compute_locale_variants
- fun:g_get_language_names
-}
-
-{
- g_get_language_names
- Memcheck:Leak
- fun:memalign
- fun:posix_memalign
- fun:slab_allocator_alloc_chunk
- fun:g_slice_alloc
- fun:g_hash_table_new_full
- fun:g_get_language_names
-}
-
-{
- g_get_language_names
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_get_language_names
-}
-
-{
- g_get_language_names
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_get_language_names
-}
-
-{
g_static_private_set
Memcheck:Leak
- fun:realloc
- fun:g_realloc
- fun:g_array_maybe_expand
- fun:g_array_set_size
+ fun:memalign
+ ...
fun:g_static_private_set
}
{
g_static_private_set
Memcheck:Leak
- fun:malloc
- fun:realloc
- fun:g_realloc
- fun:g_array_maybe_expand
- fun:g_array_set_size
+ fun:*alloc
+ ...
fun:g_static_private_set
}
{
- g_get_language_names
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_hash_table_insert_internal
- fun:g_get_language_names
-}
-
-{
- g_get_language_names
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_slice_alloc
- fun:g_hash_table_insert_internal
- fun:g_get_language_names
-}
-
-{
- g_get_language_names
+ g_static_private_set
Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_hash_table_resize
- fun:g_hash_table_insert_internal
- fun:g_get_language_names
+ fun:*alloc
+ ...
+ fun:g_intern_static_string
}
{
g_get_language_names
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_slice_alloc
- fun:g_hash_table_new_full
+ fun:*alloc
+ ...
fun:g_get_language_names
}
@@ -199,49 +133,31 @@
g_get_language_names
Memcheck:Leak
fun:memalign
- fun:posix_memalign
- fun:slab_allocator_alloc_chunk
- fun:g_slice_alloc
- fun:g_slist_prepend
- fun:g_strsplit
+ ...
fun:g_get_language_names
}
{
g_set_prgname
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strdup
+ fun:*alloc
+ ...
fun:g_set_prgname
}
{
g_set_application_name
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strdup
+ fun:*alloc
+ ...
fun:g_set_application_name
}
{
g_thread_init_glib
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_private_new_posix_impl
- fun:_g_messages_thread_init_nomessage
- fun:g_thread_init_glib
-}
-
-{
- g_thread_init_glib
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_private_new_posix_impl
- fun:_g_slice_thread_init_nomessage
+ fun:*alloc
+ ...
fun:g_thread_init_glib
}
@@ -254,197 +170,43 @@
}
{
- g_thread_init_glib
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_private_new_posix_impl
- fun:g_thread_init_glib
-}
-
-{
- g_thread_init_glib
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_mutex_new_posix_impl
- fun:_g_messages_thread_init_nomessage
- fun:g_thread_init_glib
-}
-
-{
- g_thread_init_glib
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_thread_self
- fun:g_thread_init_glib
-}
-
-{
- g_thread_init_glib
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_mutex_new_posix_impl
- fun:_g_slice_thread_init_nomessage
- fun:g_thread_init_glib
-}
-
-{
- g_thread_init_glib
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_cond_new_posix_impl
- fun:g_thread_init_glib
-}
-
-{
- g_thread_init_glib
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_mutex_new_posix_impl
- fun:g_thread_init_glib
-}
-
-{
- g_thread_init_glib
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_mutex_new_posix_impl
- fun:_g_mem_thread_init_noprivate_nomessage
- fun:g_thread_init_glib
-}
-
-{
- g_get_filename_charsets
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strdup
- fun:g_get_filename_charsets
-}
-
-{
- g_get_filename_charsets
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_get_filename_charsets
-}
-
-{
g_get_filename_charsets
Memcheck:Leak
- fun:memalign
- fun:posix_memalign
- fun:slab_allocator_alloc_chunk
- fun:g_slice_alloc
- fun:g_array_sized_new
- fun:g_static_private_set
+ fun:*alloc
+ ...
fun:g_get_filename_charsets
}
{
- g_get_filename_charsets
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_slice_alloc
- fun:g_array_sized_new
- fun:g_static_private_set
- fun:g_get_filename_charsets
-}
-
-{
- g_static_private_set
- Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_slice_alloc
- fun:g_array_sized_new
- fun:g_static_private_set
-}
-
-{
- g_static_private_get
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_thread_self
- fun:g_static_private_get
-}
-
-{
g_get_charset
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strdup
- fun:g_get_charset
-}
-
-{
- g_get_charset
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_get_charset
-}
-
-{
- g_get_charset
- Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_slice_alloc
- fun:g_array_sized_new
- fun:g_static_private_set
- fun:g_get_charset
-}
-
-{
- g_get_charset
- Memcheck:Leak
- fun:memalign
- fun:posix_memalign
- fun:slab_allocator_alloc_chunk
- fun:g_slice_alloc
- fun:g_array_sized_new
- fun:g_static_private_set
+ fun:*alloc
+ ...
fun:g_get_charset
}
{
openssl
Memcheck:Leak
- fun:malloc
- fun:CRYPTO_malloc
- fun:engine_cleanup_add_last
- fun:ENGINE_add
+ fun:*alloc
+ ...
fun:ENGINE_load_dynamic
}
{
- openssl
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:CRYPTO_malloc
- fun:ENGINE_new
- fun:ENGINE_load_dynamic
+ fun:*alloc
+ ...
+ fun:g_data_initialize
}
{
- openssl
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:CRYPTO_malloc
- obj:/usr/lib/libssl.so.0.9.8
- fun:SSL_COMP_get_compression_methods
- fun:SSL_library_init
+ fun:*alloc
+ ...
+ fun:g_resolver_get_default
}
{
@@ -452,165 +214,33 @@
Memcheck:Leak
fun:malloc
fun:CRYPTO_malloc
- fun:sk_new
- obj:/usr/lib/libssl.so.0.9.8
+ ...
fun:SSL_COMP_get_compression_methods
fun:SSL_library_init
}
{
- openssl
- Memcheck:Leak
- fun:malloc
- fun:CRYPTO_malloc
- fun:sk_new
- fun:engine_cleanup_add_last
- fun:ENGINE_add
- fun:ENGINE_load_dynamic
-}
-
-{
<insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:CRYPTO_malloc
- fun:ERR_get_state
- fun:ERR_clear_error
- fun:Curl_ossl_init
- fun:curl_global_init
-}
-
-{
- openssl
- Memcheck:Leak
- fun:malloc
+ fun:*alloc
fun:CRYPTO_malloc
- fun:lh_new
- obj:/usr/lib/libcrypto.so.0.9.8
- obj:/usr/lib/libcrypto.so.0.9.8
+ ...
fun:ERR_get_state
- fun:ERR_clear_error
- fun:Curl_ossl_init
- fun:curl_global_init
}
{
- openssl
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
+ fun:*alloc
fun:CRYPTO_malloc
- fun:lh_insert
- obj:/usr/lib/libcrypto.so.0.9.8
- fun:ERR_get_state
- fun:ERR_clear_error
+ ...
+ fun:RSA_new_method
}
{
<insert a suppression name here>
Memcheck:Leak
- fun:malloc
- fun:_dl_map_object_deps
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
- fun:do_dlopen
- fun:_dl_catch_error
- fun:dlerror_run
- fun:__libc_dlopen_mode
- fun:pthread_cancel_init
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:calloc
- fun:_dl_new_object
- fun:_dl_map_object_from_fd
- fun:_dl_map_object
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:malloc
- fun:_dl_new_object
- fun:_dl_map_object_from_fd
- fun:_dl_map_object
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:malloc
- fun:local_strdup
- fun:_dl_map_object
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:calloc
- fun:_dl_check_map_versions
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:malloc
- fun:_dl_map_object_deps
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:malloc
- fun:_dl_map_object_deps
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:malloc
- fun:_dl_new_object
- fun:_dl_map_object_from_fd
- fun:_dl_map_object
- fun:openaux
- fun:_dl_catch_error
- fun:_dl_map_object_deps
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
-}
-
-{
- <insert_a_suppression_name_here>
- Memcheck:Leak
- fun:malloc
- fun:local_strdup
- fun:_dl_map_object
- fun:openaux
- fun:_dl_catch_error
- fun:_dl_map_object_deps
- fun:dl_open_worker
- fun:_dl_catch_error
+ fun:*alloc
fun:_dl_open
}
@@ -621,20 +251,6 @@
fun:_dlerror_run
}
-{
- dlopen
- Memcheck:Leak
- fun:malloc
- fun:_dl_scope_free
- fun:_dl_map_object_deps
- fun:dl_open_worker
- fun:_dl_catch_error
- fun:_dl_open
- fun:do_dlopen
- fun:_dl_catch_error
- fun:dlerror_run
-}
-
# is that a leak in libdbus?
{
@@ -713,91 +329,172 @@
}
{
- g_quark_from_static_string
+ g_quark_from_string
Memcheck:Leak
- fun:calloc
- fun:g_malloc0
- fun:g_hash_table_new_full
- fun:g_quark_from_static_string
+ fun:*alloc
+ ...
+ fun:g_quark_from_*
}
{
- g_quark_from_static_string
+ g_get_any_init_do
Memcheck:Leak
fun:malloc
- fun:realloc
- fun:g_realloc
- fun:g_quark_from_static_string
+ fun:g_malloc
+ fun:g_strdup
+ fun:g_get_any_init_do
}
{
- g_quark_from_string
+ g_get_any_init_do
Memcheck:Leak
fun:malloc
fun:g_malloc
- fun:g_strdup
- fun:g_quark_from_string
+ fun:g_strjoinv
+ fun:g_get_any_init_do
}
{
- g_quark_from_string
+ nss
+ Memcheck:Leak
+ fun:malloc
+ fun:__nss_lookup_function
+}
+
+{
+ nss
+ Memcheck:Leak
+ fun:malloc
+ fun:tsearch
+ fun:__nss_lookup_function
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_init_with_debug_flags
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_register_static
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_add_interface_static
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_add_interface_check
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:g_type_interface_add_prerequisite
+}
+
+{
+ <insert_a_suppression_name_here>
Memcheck:Leak
fun:calloc
fun:g_malloc0
- fun:g_hash_table_new_full
- fun:g_quark_from_string
+ fun:g_type_class_ref
}
{
- g_quark_from_string
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:realloc
- fun:g_realloc
- fun:g_quark_from_string
+ fun:*alloc
+ ...
+ fun:g_*_class_intern_init
}
{
- g_quark_from_string
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_slice_alloc
- fun:g_hash_table_new_full
- fun:g_quark_from_string
+ fun:*alloc
+ ...
+ fun:type_iface_vtable_base_init_Wm
}
{
- g_get_any_init_do
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strdup
- fun:g_get_any_init_do
+ fun:*alloc
+ ...
+ fun:g_object_do_class_init
}
{
- g_get_any_init_do
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:g_malloc
- fun:g_strjoinv
- fun:g_get_any_init_do
+ fun:*alloc
+ ...
+ fun:g_object_base_class_init
}
{
- nss
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:__nss_lookup_function
+ fun:*alloc
+ ...
+ fun:g_object_class_install_property
}
{
- nss
+ <insert_a_suppression_name_here>
Memcheck:Leak
- fun:malloc
- fun:tsearch
- fun:__nss_lookup_function
+ fun:*alloc
+ ...
+ fun:soup_*_class_intern_init
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:soup_auth_manager_add_type
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:soup_auth_manager_class_intern_init
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:soup_auth_manager_ntlm_class_intern_init
+}
+
+{
+ <insert_a_suppression_name_here>
+ Memcheck:Leak
+ fun:*alloc
+ ...
+ fun:intern_header_name
}
{