diff --git a/configure.ac b/configure.ac index 52f67fff..99e8d903 100644 --- a/configure.ac +++ b/configure.ac @@ -212,18 +212,19 @@ AM_CONDITIONAL(COND_MPD, [test "x$enable_mpd" != "xno"]) dnl --- End options --- dnl Selection of local audio sound system +dnl TODO exchange oss4 with Pulseaudio case "$host" in *-*-linux-*) use_alsa=true use_oss4=false ;; *-*-kfreebsd*-*|*-*-freebsd*) - use_alsa=false - use_oss4=true + use_alsa=true + use_oss4=false ;; esac -AC_ARG_WITH(alsa, AS_HELP_STRING([--with-alsa], [use ALSA (default Linux=yes, FreeBSD=no)]), [ +AC_ARG_WITH(alsa, AS_HELP_STRING([--with-alsa], [use ALSA (default yes)]), [ AS_IF([test "x$with_alsa" = "xyes"], [use_alsa=true], [use_alsa=false]) ]) if test x$use_alsa = xtrue; then @@ -232,15 +233,6 @@ if test x$use_alsa = xtrue; then fi AM_CONDITIONAL(COND_ALSA, test x$use_alsa = xtrue) -AC_ARG_WITH(oss4, AS_HELP_STRING([--with-oss4], [use OSS4 (default Linux=no, FreeBSD=yes)]), [ - AS_IF([test "x$with_oss4" = "xyes"], [use_oss4=true], [use_oss4=false]) -]) -if test x$use_oss4 = xtrue; then - CPPFLAGS="${CPPFLAGS} -DOSS4" - AC_CHECK_HEADER(sys/soundcard.h, , AC_MSG_ERROR([sys/soundcard.h not found])) -fi -AM_CONDITIONAL(COND_OSS4, test x$use_oss4 = xtrue) - dnl Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT diff --git a/src/Makefile.am b/src/Makefile.am index f3cee113..32fe6011 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -29,10 +29,6 @@ if COND_ALSA ALSA_SRC=outputs/alsa.c endif -if COND_OSS4 -OSS4_SRC=laudio_oss4.c -endif - GPERF_FILES = \ daap_query.gperf \ rsp_query.gperf \ @@ -106,15 +102,12 @@ forked_daapd_SOURCES = main.c \ player.c player.h \ queue.c queue.h \ worker.c worker.h \ - $(ALSA_SRC) $(OSS4_SRC) \ outputs.h outputs.c \ - laudio_dummy.c \ - laudio.c laudio.h \ - outputs/raop.c outputs/streaming.c \ + outputs/raop.c outputs/streaming.c outputs/dummy.c \ + $(ALSA_SRC) $(CHROMECAST_SRC) \ evrtsp/rtsp.c evrtp/evrtsp.h evrtsp/rtsp-internal.h evrtsp/log.h \ $(SPOTIFY_SRC) \ $(LASTFM_SRC) \ - $(CHROMECAST_SRC) \ $(MPD_SRC) \ listener.c listener.h diff --git a/src/laudio.c b/src/laudio.c deleted file mode 100644 index a86ad9db..00000000 --- a/src/laudio.c +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2015 Christian Meffert - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "conffile.h" -#include "logger.h" -#include "player.h" -#include "laudio.h" - -#ifdef OSS4 -extern audio_output audio_oss4; -#endif - -extern audio_output audio_dummy; - -static audio_output *outputs[] = { -#ifdef OSS4 - &audio_oss4, -#endif - &audio_dummy, - NULL -}; - -static audio_output *output; - -struct pcm_packet -{ - uint8_t samples[STOB(AIRTUNES_V2_PACKET_SAMPLES)]; - - uint64_t rtptime; - - size_t offset; - - struct pcm_packet *next; -}; - - - -void -laudio_write(uint8_t *buf, uint64_t rtptime) -{ - output->write(buf, rtptime); -} - -uint64_t -laudio_get_pos(void) -{ - return output->pos(); -} - -void -laudio_set_volume(int vol) -{ - output->volume(vol); -} - -int -laudio_start(uint64_t cur_pos, uint64_t next_pkt) -{ - return output->start(cur_pos, next_pkt); -} - -void -laudio_stop(void) -{ - output->stop(); -} - - -int -laudio_open(void) -{ - return output->open(); -} - -void -laudio_close(void) -{ - output->close(); -} - - -int -laudio_init(laudio_status_cb cb) -{ - cfg_t *cfg_audio; - char *type; - int i; - - cfg_audio = cfg_getsec(cfg, "audio"); - type = cfg_getstr(cfg_audio, "type"); - - output = NULL; - if (type) - { - DPRINTF(E_DBG, L_LAUDIO, "Searching for local audio output: '%s'\n", type); - for (i = 0; outputs[i]; i++) - { - if (0 == strcmp(type, outputs[i]->name)) - { - output = outputs[i]; - } - } - - if (!output) - DPRINTF(E_WARN, L_LAUDIO, "No local audio output '%s' available, falling back to default output\n", type); - } - - if (!output) - { - output = outputs[0]; - } - - DPRINTF(E_INFO, L_LAUDIO, "Local audio output: '%s'\n", output->name); - - return output->init(cb, cfg_audio); -} - -void -laudio_deinit(void) -{ - output->deinit(); -} diff --git a/src/laudio.h b/src/laudio.h deleted file mode 100644 index fce80d0f..00000000 --- a/src/laudio.h +++ /dev/null @@ -1,80 +0,0 @@ - -#ifndef __LAUDIO_H__ -#define __LAUDIO_H__ - -#define LAUDIO_F_STARTED (1 << 15) - -enum laudio_state - { - LAUDIO_CLOSED = 0, - LAUDIO_STOPPING = 1, - LAUDIO_OPEN = 2, - LAUDIO_STARTED = LAUDIO_F_STARTED, - LAUDIO_RUNNING = LAUDIO_F_STARTED | 0x01, - - LAUDIO_FAILED = -1, - }; - -typedef void (*laudio_status_cb)(enum laudio_state status); - -typedef struct -{ - // Identifier of th audio output - char *name; - - // Initialization function called during startup - int (*init)(laudio_status_cb cb, cfg_t *cfg_audio); - - // Deinitialization function called at shutdown - void (*deinit)(void); - - // Function to open the output called at playback start or speaker activiation - int (*open)(void); - - // Function called after opening the output (during playback start or speaker activiation - int (*start)(uint64_t cur_pos, uint64_t next_pkt); - - // block of samples - void (*write)(uint8_t *buf, uint64_t rtptime); - - // Stopping audio playback - void (*stop)(void); - - // Closes the output - void (*close)(void); - - // Returns the rtptime of the packet thats is currently playing - uint64_t (*pos)(); - - // Sets the volum for the output - void (*volume)(int vol); -} audio_output; - -void -laudio_write(uint8_t *buf, uint64_t rtptime); - -uint64_t -laudio_get_pos(void); - -void -laudio_set_volume(int vol); - -int -laudio_start(uint64_t cur_pos, uint64_t next_pkt); - -void -laudio_stop(void); - -int -laudio_open(void); - -void -laudio_close(void); - -int -laudio_init(laudio_status_cb cb); - -void -laudio_deinit(void); - -#endif /* !__LAUDIO_H__ */ diff --git a/src/laudio_dummy.c b/src/laudio_dummy.c deleted file mode 100644 index 6f3597ea..00000000 --- a/src/laudio_dummy.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2015 Christian Meffert - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include -#include - -#include "conffile.h" -#include "logger.h" -#include "misc.h" -#include "player.h" -#include "laudio.h" - - -static enum laudio_state pcm_status; -static laudio_status_cb status_cb; - -static struct timespec timer_res; -static struct timespec ts; -static uint64_t pcmpos; - - - -static uint64_t -laudio_dummy_get_pos(void) -{ - struct timespec cur_timer_res; - struct timespec cur_ts; - uint64_t delta; - int ret; - - ret = clock_gettime_with_res(CLOCK_MONOTONIC, &cur_ts, &cur_timer_res); - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Couldn't get clock: %s\n", strerror(errno)); - return -1; - } - - delta = (cur_ts.tv_sec - ts.tv_sec) * 1000000 + (cur_ts.tv_nsec - ts.tv_nsec) / 1000; - delta = (delta * 44100) / 1000000; - - DPRINTF(E_DBG, L_LAUDIO, "Start: %" PRIu64 ", Pos: %" PRIu64 "\n", pcmpos, delta); - - return (pcmpos + delta); -} - -static void -laudio_dummy_write(uint8_t *buf, uint64_t rtptime) -{ - uint64_t pos; - - pos = laudio_dummy_get_pos(); - - if (pcm_status != LAUDIO_RUNNING && pos > (pcmpos + 88200)) - { - pcm_status = LAUDIO_RUNNING; - status_cb(LAUDIO_RUNNING); - } -} - -static void -laudio_dummy_set_volume(int vol) -{ -} - -static int -laudio_dummy_start(uint64_t cur_pos, uint64_t next_pkt) -{ - int ret; - - ret = clock_gettime_with_res(CLOCK_MONOTONIC, &ts, &timer_res); - - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Couldn't get current clock: %s\n", strerror(errno)); - return -1; - } - - pcmpos = cur_pos; - - pcm_status = LAUDIO_STARTED; - status_cb(LAUDIO_STARTED); - - return 0; -} - -static void -laudio_dummy_stop(void) -{ - pcm_status = LAUDIO_STOPPING; - status_cb(LAUDIO_STOPPING); - pcm_status = LAUDIO_OPEN; - status_cb(LAUDIO_OPEN); -} - -static int -laudio_dummy_open(void) -{ - pcm_status = LAUDIO_OPEN; - status_cb(LAUDIO_OPEN); - return 0; -} - -static void -laudio_dummy_close(void) -{ - pcm_status = LAUDIO_CLOSED; - status_cb(LAUDIO_CLOSED); -} - - -static int -laudio_dummy_init(laudio_status_cb cb, cfg_t *cfg_audio) -{ - status_cb = cb; - return 0; -} - -static void -laudio_dummy_deinit(void) -{ -} - -audio_output audio_dummy = { - .name = "dummy", - .init = &laudio_dummy_init, - .deinit = &laudio_dummy_deinit, - .start = &laudio_dummy_start, - .stop = &laudio_dummy_stop, - .open = &laudio_dummy_open, - .close = &laudio_dummy_close, - .pos = &laudio_dummy_get_pos, - .write = &laudio_dummy_write, - .volume = &laudio_dummy_set_volume, - }; diff --git a/src/laudio_oss4.c b/src/laudio_oss4.c deleted file mode 100644 index c4b6424f..00000000 --- a/src/laudio_oss4.c +++ /dev/null @@ -1,427 +0,0 @@ -/* - * Copyright (C) 2010 Julien BLACHE - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "conffile.h" -#include "logger.h" -#include "player.h" -#include "laudio.h" - - -struct pcm_packet -{ - uint8_t samples[STOB(AIRTUNES_V2_PACKET_SAMPLES)]; - - uint64_t rtptime; - - size_t offset; - - struct pcm_packet *next; -}; - -static uint64_t pcm_pos; -static uint64_t pcm_start_pos; -static int pcm_buf_threshold; -static int pcm_retry; - -static struct pcm_packet *pcm_pkt_head; -static struct pcm_packet *pcm_pkt_tail; - -static char *card_name; -static int oss_fd; - -static enum laudio_state pcm_status; -static laudio_status_cb status_cb; - - -static void -update_status(enum laudio_state status) -{ - pcm_status = status; - status_cb(status); -} - -static void -laudio_oss4_write(uint8_t *buf, uint64_t rtptime) -{ - struct pcm_packet *pkt; - int scratch; - int nsamp; - int ret; - - pkt = (struct pcm_packet *)malloc(sizeof(struct pcm_packet)); - if (!pkt) - { - DPRINTF(E_LOG, L_LAUDIO, "Out of memory for PCM pkt\n"); - - update_status(LAUDIO_FAILED); - return; - } - - memcpy(pkt->samples, buf, sizeof(pkt->samples)); - - pkt->rtptime = rtptime; - pkt->offset = 0; - pkt->next = NULL; - - if (pcm_pkt_tail) - { - pcm_pkt_tail->next = pkt; - pcm_pkt_tail = pkt; - } - else - { - pcm_pkt_head = pkt; - pcm_pkt_tail = pkt; - } - - if (pcm_pos < pcm_pkt_head->rtptime) - { - pcm_pos += AIRTUNES_V2_PACKET_SAMPLES; - - return; - } - else if ((pcm_status != LAUDIO_RUNNING) && (pcm_pos >= pcm_start_pos)) - { - /* Start audio output */ - scratch = PCM_ENABLE_OUTPUT; - ret = ioctl(oss_fd, SNDCTL_DSP_SETTRIGGER, &scratch); - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Could not enable output: %s\n", strerror(errno)); - - update_status(LAUDIO_FAILED); - return; - } - - update_status(LAUDIO_RUNNING); - } - - pkt = pcm_pkt_head; - - while (pkt) - { - nsamp = write(oss_fd, pkt->samples + pkt->offset, sizeof(pkt->samples) - pkt->offset); - if (nsamp < 0) - { - if (errno == EAGAIN) - { - pcm_retry++; - - if (pcm_retry < 10) - return; - } - - DPRINTF(E_LOG, L_LAUDIO, "Write error: %s\n", strerror(errno)); - - update_status(LAUDIO_FAILED); - return; - } - - pcm_retry = 0; - - pkt->offset += nsamp; - - nsamp = BTOS(nsamp); - pcm_pos += nsamp; - - if (pkt->offset == sizeof(pkt->samples)) - { - pcm_pkt_head = pkt->next; - - if (pkt == pcm_pkt_tail) - pcm_pkt_tail = NULL; - - free(pkt); - - pkt = pcm_pkt_head; - } - - /* Don't let the buffer fill up too much */ - if (nsamp == AIRTUNES_V2_PACKET_SAMPLES) - break; - } -} - -static uint64_t -laudio_oss4_get_pos(void) -{ - int delay; - int ret; - - ret = ioctl(oss_fd, SNDCTL_DSP_GETODELAY, &delay); - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Could not obtain output delay: %s\n", strerror(errno)); - - return pcm_pos; - } - - return pcm_pos - BTOS(delay); -} - -static void -laudio_oss4_set_volume(int vol) -{ - int oss_vol; - int ret; - - vol = vol & 0xff; - oss_vol = vol | (vol << 8); - - ret = ioctl(oss_fd, SNDCTL_DSP_SETPLAYVOL, &oss_vol); - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Could not set volume: %s\n", strerror(errno)); - - return; - } - - DPRINTF(E_DBG, L_LAUDIO, "Setting PCM volume to %d (real: %d)\n", vol, (oss_vol & 0xff)); -} - -static int -laudio_oss4_start(uint64_t cur_pos, uint64_t next_pkt) -{ - int scratch; - int ret; - - DPRINTF(E_DBG, L_LAUDIO, "PCM will start after %d samples (%d packets)\n", pcm_buf_threshold, pcm_buf_threshold / AIRTUNES_V2_PACKET_SAMPLES); - - /* Make pcm_pos the rtptime of the packet containing cur_pos */ - pcm_pos = next_pkt; - while (pcm_pos > cur_pos) - pcm_pos -= AIRTUNES_V2_PACKET_SAMPLES; - - pcm_start_pos = next_pkt + pcm_buf_threshold; - - /* FIXME check for OSS - Compensate threshold, as it's taken into account by snd_pcm_delay() */ - pcm_pos += pcm_buf_threshold; - - DPRINTF(E_DBG, L_LAUDIO, "PCM pos %" PRIu64 ", start pos %" PRIu64 "\n", pcm_pos, pcm_start_pos); - - pcm_pkt_head = NULL; - pcm_pkt_tail = NULL; - - pcm_retry = 0; - - scratch = 0; - ret = ioctl(oss_fd, SNDCTL_DSP_SETTRIGGER, &scratch); - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Could not set trigger: %s\n", strerror(errno)); - - return -1; - } - - update_status(LAUDIO_STARTED); - - return 0; -} - -static void -laudio_oss4_stop(void) -{ - struct pcm_packet *pkt; - int ret; - - update_status(LAUDIO_STOPPING); - - ret = ioctl(oss_fd, SNDCTL_DSP_HALT_OUTPUT, NULL); - if (ret < 0) - DPRINTF(E_LOG, L_LAUDIO, "Failed to halt output: %s\n", strerror(errno)); - - for (pkt = pcm_pkt_head; pcm_pkt_head; pkt = pcm_pkt_head) - { - pcm_pkt_head = pkt->next; - - free(pkt); - } - - pcm_pkt_head = NULL; - pcm_pkt_tail = NULL; - - update_status(LAUDIO_OPEN); -} - -static int -laudio_oss4_open(void) -{ - audio_buf_info bi; - oss_sysinfo si; - int scratch; - int ret; - - oss_fd = open(card_name, O_RDWR | O_NONBLOCK); - if (oss_fd < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Could not open sound device: %s\n", strerror(errno)); - - return -1; - } - - ret = ioctl(oss_fd, SNDCTL_SYSINFO, &si); - if ((ret < 0) || (si.versionnum < 0x040000)) - { - DPRINTF(E_LOG, L_LAUDIO, "Your OSS version (%s) is unavailable or too old; version 4.0.0+ is required\n", si.version); - - goto out_fail; - } - - scratch = 0; - ret = ioctl(oss_fd, SNDCTL_DSP_SETTRIGGER, &scratch); - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Could not set trigger: %s\n", strerror(errno)); - - goto out_fail; - } - - scratch = AFMT_S16_LE; - errno = 0; - ret = ioctl(oss_fd, SNDCTL_DSP_SETFMT, &scratch); - if ((ret < 0) || (scratch != AFMT_S16_LE)) - { - if (errno) - DPRINTF(E_LOG, L_LAUDIO, "Could not set sample format (S16 LE): %s\n", strerror(errno)); - else - DPRINTF(E_LOG, L_LAUDIO, "Sample format S16 LE not supported\n"); - - goto out_fail; - } - - scratch = 2; - errno = 0; - ret = ioctl(oss_fd, SNDCTL_DSP_CHANNELS, &scratch); - if ((ret < 0) || (scratch != 2)) - { - if (errno) - DPRINTF(E_LOG, L_LAUDIO, "Could not set stereo: %s\n", strerror(errno)); - else - DPRINTF(E_LOG, L_LAUDIO, "Stereo not supported\n"); - - goto out_fail; - } - - scratch = 44100; - errno = 0; - ret = ioctl(oss_fd, SNDCTL_DSP_SPEED, &scratch); - if ((ret < 0) || (scratch != 44100)) - { - if (errno) - DPRINTF(E_LOG, L_LAUDIO, "Could not set speed (44100): %s\n", strerror(errno)); - else - DPRINTF(E_LOG, L_LAUDIO, "Sample rate 44100 not supported\n"); - - goto out_fail; - } - - ret = ioctl(oss_fd, SNDCTL_DSP_GETOSPACE, &bi); - if (ret < 0) - { - DPRINTF(E_LOG, L_LAUDIO, "Couldn't get output buffer status: %s\n", strerror(errno)); - - goto out_fail; - } - - pcm_buf_threshold = (BTOS(bi.bytes) / AIRTUNES_V2_PACKET_SAMPLES) * AIRTUNES_V2_PACKET_SAMPLES; - - update_status(LAUDIO_OPEN); - - return 0; - - out_fail: - close(oss_fd); - oss_fd = -1; - - return -1; -} - -static void -laudio_oss4_close(void) -{ - struct pcm_packet *pkt; - int ret; - - ret = ioctl(oss_fd, SNDCTL_DSP_HALT_OUTPUT, NULL); - if (ret < 0) - DPRINTF(E_LOG, L_LAUDIO, "Failed to halt output: %s\n", strerror(errno)); - - close(oss_fd); - oss_fd = -1; - - for (pkt = pcm_pkt_head; pcm_pkt_head; pkt = pcm_pkt_head) - { - pcm_pkt_head = pkt->next; - - free(pkt); - } - - pcm_pkt_head = NULL; - pcm_pkt_tail = NULL; - - update_status(LAUDIO_CLOSED); -} - - -static int -laudio_oss4_init(laudio_status_cb cb, cfg_t *cfg_audio) -{ - status_cb = cb; - - card_name = cfg_getstr(cfg_audio, "card"); - - return 0; -} - -static void -laudio_oss4_deinit(void) -{ - /* EMPTY */ -} - -audio_output audio_oss4 = { - .name = "oss4", - .init = &laudio_oss4_init, - .deinit = &laudio_oss4_deinit, - .start = &laudio_oss4_start, - .stop = &laudio_oss4_stop, - .open = &laudio_oss4_open, - .close = &laudio_oss4_close, - .pos = &laudio_oss4_get_pos, - .write = &laudio_oss4_write, - .volume = &laudio_oss4_set_volume, - }; - diff --git a/src/outputs.c b/src/outputs.c index fd1b2fdd..0ca17fbc 100644 --- a/src/outputs.c +++ b/src/outputs.c @@ -33,35 +33,25 @@ extern struct output_definition output_raop; extern struct output_definition output_streaming; -#ifdef CHROMECAST -extern struct output_definition output_cast; -#endif +extern struct output_definition output_dummy; #ifdef ALSA extern struct output_definition output_alsa; #endif -/* TODO -#ifdef OSS4 -extern struct output_definition output_oss4; +#ifdef CHROMECAST +extern struct output_definition output_cast; #endif -extern struct output_definition output_dummy; -*/ // Must be in sync with enum output_types static struct output_definition *outputs[] = { &output_raop, &output_streaming, -#ifdef CHROMECAST - &output_cast, -#endif + &output_dummy, #ifdef ALSA &output_alsa, #endif -/* TODO -#ifdef OSS4 - &output_oss4, +#ifdef CHROMECAST + &output_cast, #endif - &output_dummy, -*/ NULL }; diff --git a/src/outputs.h b/src/outputs.h index 644cc1a9..518a76ac 100644 --- a/src/outputs.h +++ b/src/outputs.h @@ -49,16 +49,13 @@ enum output_types { OUTPUT_TYPE_RAOP, OUTPUT_TYPE_STREAMING, -#ifdef CHROMECAST - OUTPUT_TYPE_CAST, -#endif + OUTPUT_TYPE_DUMMY, #ifdef ALSA OUTPUT_TYPE_ALSA, #endif -/* TODO - OUTPUT_TYPE_OSS, - OUTPUT_TYPE_DUMMY, -*/ +#ifdef CHROMECAST + OUTPUT_TYPE_CAST, +#endif }; /* Output session state */ diff --git a/src/outputs/dummy.c b/src/outputs/dummy.c new file mode 100644 index 00000000..058d0455 --- /dev/null +++ b/src/outputs/dummy.c @@ -0,0 +1,296 @@ +/* + * Copyright (C) 2016 Espen Jürgensen + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* This file includes much of the boilerplate code required for making an + * audio output for forked-daapd. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "conffile.h" +#include "logger.h" +#include "player.h" +#include "outputs.h" + +struct dummy_session +{ + enum output_device_state state; + + struct event *deferredev; + output_status_cb defer_cb; + + /* Do not dereference - only passed to the status cb */ + struct output_device *device; + struct output_session *output_session; + output_status_cb status_cb; +}; + +/* From player.c */ +extern struct event_base *evbase_player; + +struct dummy_session *sessions; + +/* Forwards */ +static void +defer_cb(int fd, short what, void *arg); + +/* ---------------------------- SESSION HANDLING ---------------------------- */ + +static void +dummy_session_free(struct dummy_session *ds) +{ + event_free(ds->deferredev); + + free(ds->output_session); + free(ds); + + ds = NULL; +} + +static void +dummy_session_cleanup(struct dummy_session *ds) +{ + // Normally some here code to remove from linked list - here we just say: + sessions = NULL; + + dummy_session_free(ds); +} + +static struct dummy_session * +dummy_session_make(struct output_device *device, output_status_cb cb) +{ + struct output_session *os; + struct dummy_session *ds; + + os = calloc(1, sizeof(struct output_session)); + ds = calloc(1, sizeof(struct dummy_session)); + if (!os || !ds) + { + DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy session\n"); + return NULL; + } + + ds->deferredev = evtimer_new(evbase_player, defer_cb, ds); + if (!ds->deferredev) + { + DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy deferred event\n"); + free(os); + free(ds); + return NULL; + } + + os->session = ds; + os->type = device->type; + + ds->output_session = os; + ds->state = OUTPUT_STATE_CONNECTED; + ds->device = device; + ds->status_cb = cb; + + sessions = ds; + + return ds; +} + + +/* ---------------------------- STATUS HANDLERS ----------------------------- */ + +// Maps our internal state to the generic output state and then makes a callback +// to the player to tell that state +static void +defer_cb(int fd, short what, void *arg) +{ + struct dummy_session *ds = arg; + + if (ds->defer_cb) + ds->defer_cb(ds->device, ds->output_session, ds->state); + + if (ds->state == OUTPUT_STATE_STOPPED) + dummy_session_cleanup(ds); +} + +static void +dummy_status(struct dummy_session *ds) +{ + ds->defer_cb = ds->status_cb; + event_active(ds->deferredev, 0, 0); + ds->status_cb = NULL; +} + + +/* ------------------ INTERFACE FUNCTIONS CALLED BY OUTPUTS.C --------------- */ + +static int +dummy_device_start(struct output_device *device, output_status_cb cb, uint64_t rtptime) +{ + struct dummy_session *ds; + + ds = dummy_session_make(device, cb); + if (!ds) + return -1; + + dummy_status(ds); + + return 0; +} + +static void +dummy_device_stop(struct output_session *session) +{ + struct dummy_session *ds = session->session; + + ds->state = OUTPUT_STATE_STOPPED; + dummy_status(ds); +} + +static int +dummy_device_probe(struct output_device *device, output_status_cb cb) +{ + struct dummy_session *ds; + + ds = dummy_session_make(device, cb); + if (!ds) + return -1; + + ds->status_cb = cb; + ds->state = OUTPUT_STATE_STOPPED; + + dummy_status(ds); + + return 0; +} + +static int +dummy_device_volume_set(struct output_device *device, output_status_cb cb) +{ + struct dummy_session *ds; + + if (!device->session || !device->session->session) + return 0; + + ds = device->session->session; + + ds->status_cb = cb; + dummy_status(ds); + + return 1; +} + +static void +dummy_playback_start(uint64_t next_pkt, struct timespec *ts) +{ + struct dummy_session *ds = sessions; + + if (!sessions) + return; + + ds->state = OUTPUT_STATE_STREAMING; + dummy_status(ds); +} + +static void +dummy_playback_stop(void) +{ + struct dummy_session *ds = sessions; + + if (!sessions) + return; + + ds->state = OUTPUT_STATE_CONNECTED; + dummy_status(ds); +} + +static void +dummy_set_status_cb(struct output_session *session, output_status_cb cb) +{ + struct dummy_session *ds = session->session; + + ds->status_cb = cb; +} + +static int +dummy_init(void) +{ + struct output_device *device; + cfg_t *cfg_audio; + char *nickname; + char *type; + + cfg_audio = cfg_getsec(cfg, "audio"); + type = cfg_getstr(cfg_audio, "type"); + if (!type || (strcasecmp(type, "dummy") != 0)) + return -1; + + nickname = cfg_getstr(cfg_audio, "nickname"); + + device = calloc(1, sizeof(struct output_device)); + if (!device) + { + DPRINTF(E_LOG, L_LAUDIO, "Out of memory for dummy device\n"); + return -1; + } + + device->id = 0; + device->name = nickname; + device->type = OUTPUT_TYPE_DUMMY; + device->type_name = outputs_name(device->type); + device->advertised = 1; + device->has_video = 0; + + DPRINTF(E_INFO, L_LAUDIO, "Adding dummy output device '%s'\n", nickname); + + player_device_add(device); + + return 0; +} + +static void +dummy_deinit(void) +{ + return; +} + +struct output_definition output_dummy = +{ + .name = "dummy", + .type = OUTPUT_TYPE_DUMMY, + .priority = 99, + .disabled = 0, + .init = dummy_init, + .deinit = dummy_deinit, + .device_start = dummy_device_start, + .device_stop = dummy_device_stop, + .device_probe = dummy_device_probe, + .device_volume_set = dummy_device_volume_set, + .playback_start = dummy_playback_start, + .playback_stop = dummy_playback_stop, + .status_cb = dummy_set_status_cb, +};