[pipe] Add a pipe input with autostart capabilities
This commit is contained in:
parent
938e197fa4
commit
9aede45a12
|
@ -116,8 +116,9 @@ library {
|
|||
# File types the scanner should ignore
|
||||
# Non-audio files will never be added to the database, but here you
|
||||
# can prevent the scanner from even probing them. This might improve
|
||||
# scan time. By default .db, .ini, .db-journal and .pdf are ignored.
|
||||
# filetypes_ignore = { ".db", ".ini", ".db-journal", ".pdf" }
|
||||
# scan time. By default .db, .ini, .db-journal, .pdf and .metadata are
|
||||
# ignored.
|
||||
# filetypes_ignore = { ".db", ".ini", ".db-journal", ".pdf", ".metadata" }
|
||||
|
||||
# File paths the scanner should ignore
|
||||
# If you want to exclude files on a more advanced basis you can enter
|
||||
|
@ -153,6 +154,11 @@ library {
|
|||
# no_decode = { "format", "format" }
|
||||
# Formats that should always be decoded
|
||||
# force_decode = { "format", "format" }
|
||||
|
||||
# Watch named pipes in the library for data and autostart playback when
|
||||
# there is data to be read. To exclude specific pipes from watching,
|
||||
# consider using the above _ignore options.
|
||||
# pipe_autostart = true
|
||||
}
|
||||
|
||||
# Local audio output
|
||||
|
|
|
@ -100,7 +100,6 @@ forked_daapd_SOURCES = main.c \
|
|||
http.c http.h \
|
||||
dmap_common.c dmap_common.h \
|
||||
transcode.c transcode.h \
|
||||
pipe.c pipe.h \
|
||||
artwork.c artwork.h \
|
||||
misc.c misc.h \
|
||||
rng.c rng.h \
|
||||
|
@ -109,7 +108,7 @@ forked_daapd_SOURCES = main.c \
|
|||
player.c player.h \
|
||||
worker.c worker.h \
|
||||
input.h input.c \
|
||||
inputs/file_http.c \
|
||||
inputs/file_http.c inputs/pipe.c \
|
||||
outputs.h outputs.c \
|
||||
outputs/raop.c outputs/streaming.c outputs/dummy.c outputs/fifo.c \
|
||||
$(ALSA_SRC) $(PULSEAUDIO_SRC) $(CHROMECAST_SRC) \
|
||||
|
|
|
@ -82,13 +82,14 @@ static cfg_opt_t sec_library[] =
|
|||
CFG_STR("name_radio", "Radio", CFGF_NONE),
|
||||
CFG_STR_LIST("artwork_basenames", "{artwork,cover,Folder}", CFGF_NONE),
|
||||
CFG_BOOL("artwork_individual", cfg_false, CFGF_NONE),
|
||||
CFG_STR_LIST("filetypes_ignore", "{.db,.ini,.db-journal,.pdf}", CFGF_NONE),
|
||||
CFG_STR_LIST("filetypes_ignore", "{.db,.ini,.db-journal,.pdf,.metadata}", CFGF_NONE),
|
||||
CFG_STR_LIST("filepath_ignore", NULL, CFGF_NONE),
|
||||
CFG_BOOL("filescan_disable", cfg_false, CFGF_NONE),
|
||||
CFG_BOOL("itunes_overrides", cfg_false, CFGF_NONE),
|
||||
CFG_BOOL("itunes_smartpl", cfg_false, CFGF_NONE),
|
||||
CFG_STR_LIST("no_decode", NULL, CFGF_NONE),
|
||||
CFG_STR_LIST("force_decode", NULL, CFGF_NONE),
|
||||
CFG_BOOL("pipe_autostart", cfg_true, CFGF_NONE),
|
||||
CFG_END()
|
||||
};
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
extern struct input_definition input_file;
|
||||
extern struct input_definition input_http;
|
||||
extern struct input_definition input_pipe;
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
extern struct input_definition input_spotify;
|
||||
#endif
|
||||
|
@ -54,6 +55,7 @@ extern struct input_definition input_spotify;
|
|||
static struct input_definition *inputs[] = {
|
||||
&input_file,
|
||||
&input_http,
|
||||
&input_pipe,
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
&input_spotify,
|
||||
#endif
|
||||
|
@ -130,6 +132,9 @@ map_data_kind(int data_kind)
|
|||
case DATA_KIND_HTTP:
|
||||
return INPUT_TYPE_HTTP;
|
||||
|
||||
case DATA_KIND_PIPE:
|
||||
return INPUT_TYPE_PIPE;
|
||||
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
case DATA_KIND_SPOTIFY:
|
||||
return INPUT_TYPE_SPOTIFY;
|
||||
|
|
|
@ -13,6 +13,7 @@ enum input_types
|
|||
{
|
||||
INPUT_TYPE_FILE,
|
||||
INPUT_TYPE_HTTP,
|
||||
INPUT_TYPE_PIPE,
|
||||
#ifdef HAVE_SPOTIFY_H
|
||||
INPUT_TYPE_SPOTIFY,
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,467 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Espen Jurgensen
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <event2/event.h>
|
||||
#include <event2/buffer.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "logger.h"
|
||||
#include "db.h"
|
||||
#include "conffile.h"
|
||||
#include "listener.h"
|
||||
#include "player.h"
|
||||
#include "input.h"
|
||||
|
||||
// Maximum number of pipes to watch for data
|
||||
#define PIPE_MAX_OPEN 4
|
||||
// Max number of bytes to read from a pipe at a time (
|
||||
#define PIPE_READ_MAX 65536
|
||||
|
||||
// filescanner event base, from filescanner.c
|
||||
// TODO don't use filescanner thread/base
|
||||
extern struct event_base *evbase_scan;
|
||||
|
||||
enum pipestate
|
||||
{
|
||||
PIPE_NEW = (1 << 0),
|
||||
PIPE_DEL = (1 << 1),
|
||||
PIPE_OPEN = (1 << 2),
|
||||
};
|
||||
|
||||
struct pipe
|
||||
{
|
||||
int id;
|
||||
int fd;
|
||||
char *path;
|
||||
enum pipestate state;
|
||||
struct event *ev;
|
||||
// TODO mutex
|
||||
|
||||
struct pipe *next;
|
||||
};
|
||||
|
||||
// From config - should we watch library pipes for data or only start on request
|
||||
static int pipe_autostart;
|
||||
|
||||
// Global list of pipes we are watching. If watching/autostart is disabled this
|
||||
// will just point to the currently playing pipe (if any).
|
||||
static struct pipe *pipelist;
|
||||
|
||||
|
||||
/* -------------------------------- HELPERS ------------------------------- */
|
||||
|
||||
static struct pipe *
|
||||
pipe_new(const char *path, int id)
|
||||
{
|
||||
struct pipe *pipe;
|
||||
|
||||
pipe = calloc(1, sizeof(struct pipe));
|
||||
pipe->path = strdup(path);
|
||||
pipe->id = id;
|
||||
pipe->fd = -1;
|
||||
pipe->state = PIPE_NEW;
|
||||
pipe->next = pipelist;
|
||||
pipelist = pipe;
|
||||
|
||||
return pipe;
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_free(struct pipe *pipe)
|
||||
{
|
||||
free(pipe->path);
|
||||
free(pipe);
|
||||
}
|
||||
|
||||
static struct pipe *
|
||||
pipe_find(int id)
|
||||
{
|
||||
struct pipe *pipe;
|
||||
|
||||
for (pipe = pipelist; pipe; pipe = pipe->next)
|
||||
{
|
||||
if (id == pipe->id)
|
||||
return pipe;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------- PIPE WATCHING ---------------------------- */
|
||||
/* Thread: filescanner */
|
||||
|
||||
// Updates pipelist with pipe items from the db. Pipes that are no longer in
|
||||
// the db get marked with PIPE_DEL. Returns count of pipes that should be
|
||||
// watched (may or may not equal length of pipelist)
|
||||
static int
|
||||
pipe_enum(void)
|
||||
{
|
||||
struct query_params qp;
|
||||
struct db_media_file_info dbmfi;
|
||||
struct pipe *pipe;
|
||||
char filter[32];
|
||||
int count;
|
||||
int id;
|
||||
int ret;
|
||||
|
||||
memset(&qp, 0, sizeof(struct query_params));
|
||||
qp.type = Q_ITEMS;
|
||||
qp.filter = filter;
|
||||
|
||||
snprintf(filter, sizeof(filter), "f.data_kind = %d", DATA_KIND_PIPE);
|
||||
|
||||
ret = db_query_start(&qp);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
for (pipe = pipelist; pipe; pipe = pipe->next)
|
||||
pipe->state = PIPE_DEL;
|
||||
|
||||
count = 0;
|
||||
while (((ret = db_query_fetch_file(&qp, &dbmfi)) == 0) && (dbmfi.id))
|
||||
{
|
||||
ret = safe_atoi32(dbmfi.id, &id);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
|
||||
if ((pipe = pipe_find(id)))
|
||||
{
|
||||
pipe->state = PIPE_OPEN;
|
||||
continue;
|
||||
}
|
||||
|
||||
pipe = pipe_new(dbmfi.path, id);
|
||||
}
|
||||
|
||||
db_query_end(&qp);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Some data arrived on a pipe we watch - let's autostart playback
|
||||
static void
|
||||
pipe_read_cb(evutil_socket_t fd, short event, void *arg)
|
||||
{
|
||||
struct pipe *pipe = arg;
|
||||
struct player_status status;
|
||||
struct db_queue_item *queue_item;
|
||||
int ret;
|
||||
|
||||
DPRINTF(E_DBG, L_PLAYER, "Autostarting pipe %d, %d\n", (int) fd, (int)event);
|
||||
|
||||
ret = player_get_status(&status);
|
||||
if ((ret < 0) || (status.status == PLAY_PLAYING))
|
||||
return;
|
||||
|
||||
db_queue_clear();
|
||||
|
||||
ret = db_queue_add_by_fileid(pipe->id, 0, 0);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
queue_item = db_queue_fetch_byfileid(pipe->id);
|
||||
if (!queue_item)
|
||||
return;
|
||||
|
||||
player_playback_start_byitem(queue_item);
|
||||
|
||||
free_queue_item(queue_item, 0);
|
||||
}
|
||||
|
||||
/* Opens a pipe and starts watching it for data */
|
||||
static int
|
||||
pipe_open(struct pipe *pipe)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
DPRINTF(E_DBG, L_PLAYER, "(Re)opening pipe: '%s'\n", pipe->path);
|
||||
|
||||
if (lstat(pipe->path, &sb) < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not lstat() '%s': %s\n", pipe->path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISFIFO(sb.st_mode))
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Source type is pipe, but path is not a fifo: %s\n", pipe->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pipe->fd = open(pipe->path, O_RDONLY | O_NONBLOCK);
|
||||
if (pipe->fd < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not open pipe for reading '%s': %s\n", pipe->path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
pipe->state = PIPE_OPEN;
|
||||
|
||||
if (!pipe_autostart)
|
||||
return 0; // All done
|
||||
|
||||
pipe->ev = event_new(evbase_scan, pipe->fd, EV_READ, pipe_read_cb, pipe);
|
||||
if (!pipe->ev)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not watch pipe for new data '%s'\n", pipe->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
event_add(pipe->ev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_close(struct pipe *pipe)
|
||||
{
|
||||
if (pipe->fd < 0)
|
||||
return;
|
||||
|
||||
if (pipe->ev)
|
||||
event_free(pipe->ev);
|
||||
|
||||
close(pipe->fd);
|
||||
|
||||
pipe->fd = -1;
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_remove(struct pipe *pipe)
|
||||
{
|
||||
struct pipe *p;
|
||||
|
||||
pipe_close(pipe);
|
||||
|
||||
if (pipe == pipelist)
|
||||
pipelist = pipe->next;
|
||||
else
|
||||
{
|
||||
for (p = pipelist; p && (p->next != pipe); p = p->next)
|
||||
; /* EMPTY */
|
||||
|
||||
if (!p)
|
||||
{
|
||||
DPRINTF(E_LOG, L_REMOTE, "WARNING: pipe not found in list; BUG!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
p->next = pipe->next;
|
||||
}
|
||||
|
||||
pipe_free(pipe);
|
||||
}
|
||||
|
||||
static void
|
||||
pipe_listener_cb(enum listener_event_type type)
|
||||
{
|
||||
struct pipe *pipe;
|
||||
struct pipe *next;
|
||||
int count;
|
||||
|
||||
count = pipe_enum(); // Count does not include pipes with state PIPE_DEL
|
||||
if (count < 0)
|
||||
return;
|
||||
|
||||
for (pipe = pipelist; pipe; pipe = next)
|
||||
{
|
||||
next = pipe->next;
|
||||
|
||||
DPRINTF(E_DBG, L_PLAYER, "Processing pipe '%s', state is %d\n", pipe->path, pipe->state);
|
||||
|
||||
if ((pipe->state == PIPE_NEW) && (count > PIPE_MAX_OPEN))
|
||||
DPRINTF(E_LOG, L_PLAYER, "Max open pipes reached, will not watch %s\n", pipe->path);
|
||||
else if (pipe->state == PIPE_NEW)
|
||||
pipe_open(pipe);
|
||||
else if (pipe->state == PIPE_DEL)
|
||||
pipe_remove(pipe); // Note: Will free pipe
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------- PIPE INPUT INTERFACE ------------------------ */
|
||||
/* Thread: player/input */
|
||||
|
||||
static int
|
||||
pipe_metadata_open(struct pipe *pipe)
|
||||
{
|
||||
// char md_pipe_path[PATH_MAX]; //TODO PATH_MAX - limits.h?
|
||||
/* snprintf(md_pipe_path, sizeof(md_pipe_path), "%s.metadata", pipe->path);
|
||||
|
||||
fd = pipe_open(md_pipe_path); // TODO avoid lstat error
|
||||
if (fd < 0)
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
setup(struct player_source *ps)
|
||||
{
|
||||
struct pipe *pipe;
|
||||
int ret;
|
||||
|
||||
if (pipe_autostart)
|
||||
{
|
||||
pipe = pipe_find(ps->id);
|
||||
if (!pipe)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Unknown pipe '%s'\n", ps->path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
pipe = pipe_new(ps->path, ps->id);
|
||||
|
||||
if (pipe->state != PIPE_OPEN)
|
||||
{
|
||||
ret = pipe_open(pipe);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
pipe_metadata_open(pipe);
|
||||
|
||||
if (pipe->ev)
|
||||
event_del(pipe->ev); // Avoids autostarting pipe if manually started by user
|
||||
|
||||
ps->setup_done = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
start(struct player_source *ps)
|
||||
{
|
||||
struct pipe *pipe;
|
||||
struct evbuffer *evbuf;
|
||||
int ret;
|
||||
|
||||
pipe = pipe_find(ps->id);
|
||||
if (!pipe)
|
||||
return -1;
|
||||
|
||||
evbuf = evbuffer_new();
|
||||
if (!evbuf)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Out of memory for pipe evbuf\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = -1;
|
||||
while (!input_loop_break)
|
||||
{
|
||||
ret = evbuffer_read(evbuf, pipe->fd, PIPE_READ_MAX);
|
||||
if ( (ret == 0) || ((ret < 0) && (errno == EAGAIN)) )
|
||||
{
|
||||
input_wait();
|
||||
continue;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not read from pipe: %s\n", strerror(errno));
|
||||
break;
|
||||
}
|
||||
|
||||
ret = input_write(evbuf, 0);
|
||||
if (ret < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
evbuffer_free(evbuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
stop(struct player_source *ps)
|
||||
{
|
||||
struct pipe *pipe;
|
||||
|
||||
DPRINTF(E_DBG, L_PLAYER, "Stopping pipe\n");
|
||||
|
||||
pipe = pipe_find(ps->id);
|
||||
if (pipe_autostart)
|
||||
{
|
||||
// Reopen pipe since if I just readd the event it instantly makes the
|
||||
// callback (probably something I have missed...)
|
||||
pipe_close(pipe);
|
||||
pipe_open(pipe);
|
||||
}
|
||||
else
|
||||
{
|
||||
pipe_remove(pipe);
|
||||
}
|
||||
|
||||
ps->setup_done = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Thread: main
|
||||
static int
|
||||
init(void)
|
||||
{
|
||||
pipe_autostart = cfg_getbool(cfg_getsec(cfg, "library"), "pipe_autostart");
|
||||
|
||||
if (pipe_autostart)
|
||||
return listener_add(pipe_listener_cb, LISTENER_DATABASE);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
deinit(void)
|
||||
{
|
||||
struct pipe *pipe;
|
||||
|
||||
for (pipe = pipelist; pipelist; pipe = pipelist)
|
||||
{
|
||||
pipelist = pipe->next;
|
||||
pipe_remove(pipe);
|
||||
}
|
||||
|
||||
if (pipe_autostart)
|
||||
listener_remove(pipe_listener_cb);
|
||||
}
|
||||
|
||||
struct input_definition input_pipe =
|
||||
{
|
||||
.name = "pipe",
|
||||
.type = INPUT_TYPE_PIPE,
|
||||
.disabled = 0,
|
||||
.setup = setup,
|
||||
.start = start,
|
||||
.stop = stop,
|
||||
.init = init,
|
||||
.deinit = deinit,
|
||||
};
|
||||
|
129
src/pipe.c
129
src/pipe.c
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Espen Jürgensen <espenjurgensen@gmail.com>
|
||||
*
|
||||
* 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "pipe.h"
|
||||
#include "logger.h"
|
||||
|
||||
#define PIPE_BUFFER_SIZE 8192
|
||||
|
||||
static int g_fd = -1;
|
||||
static uint16_t *g_buf = NULL;
|
||||
|
||||
int
|
||||
pipe_setup(const char *path)
|
||||
{
|
||||
struct stat sb;
|
||||
|
||||
if (!path)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Path to pipe is NULL\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
DPRINTF(E_DBG, L_PLAYER, "Setting up pipe: %s\n", path);
|
||||
|
||||
if (lstat(path, &sb) < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not lstat() '%s': %s\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!S_ISFIFO(sb.st_mode))
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Source type is pipe, but path is not a fifo: %s\n", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pipe_cleanup();
|
||||
|
||||
g_fd = open(path, O_RDONLY | O_NONBLOCK);
|
||||
if (g_fd < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not open pipe for reading '%s': %s\n", path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_buf = (uint16_t *)malloc(PIPE_BUFFER_SIZE);
|
||||
if (!g_buf)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Out of memory for buffer\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
pipe_cleanup(void)
|
||||
{
|
||||
if (g_fd >= 0)
|
||||
close(g_fd);
|
||||
g_fd = -1;
|
||||
|
||||
if (g_buf)
|
||||
free(g_buf);
|
||||
g_buf = NULL;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
pipe_audio_get(struct evbuffer *evbuf, int wanted)
|
||||
{
|
||||
int got;
|
||||
|
||||
if (wanted > PIPE_BUFFER_SIZE)
|
||||
wanted = PIPE_BUFFER_SIZE;
|
||||
|
||||
got = read(g_fd, g_buf, wanted);
|
||||
|
||||
if ((got < 0) && (errno != EAGAIN))
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Could not read from pipe: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If the other end of the pipe is not writing or the read was blocked,
|
||||
// we just return silence
|
||||
if (got <= 0)
|
||||
{
|
||||
memset(g_buf, 0, wanted);
|
||||
got = wanted;
|
||||
}
|
||||
|
||||
evbuffer_add(evbuf, g_buf, got);
|
||||
|
||||
return got;
|
||||
}
|
||||
|
16
src/pipe.h
16
src/pipe.h
|
@ -1,16 +0,0 @@
|
|||
|
||||
#ifndef __PIPE_H__
|
||||
#define __PIPE_H__
|
||||
|
||||
#include <event2/buffer.h>
|
||||
|
||||
int
|
||||
pipe_setup(const char *path);
|
||||
|
||||
void
|
||||
pipe_cleanup(void);
|
||||
|
||||
int
|
||||
pipe_audio_get(struct evbuffer *evbuf, int wanted);
|
||||
|
||||
#endif /* !__PIPE_H__ */
|
12
src/player.c
12
src/player.c
|
@ -1116,15 +1116,21 @@ playback_write(void)
|
|||
pb_buffer_offset = 0;
|
||||
pb_writes_pending--;
|
||||
}
|
||||
else if ((got < 0) || (pb_writes_pending > PLAYER_WRITES_PENDING_MAX))
|
||||
else if (got < 0)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Error reading from source, aborting playback (%d)\n", pb_writes_pending);
|
||||
DPRINTF(E_LOG, L_PLAYER, "Error reading from source, aborting playback\n");
|
||||
playback_abort();
|
||||
return;
|
||||
}
|
||||
else if (pb_writes_pending > PLAYER_WRITES_PENDING_MAX)
|
||||
{
|
||||
DPRINTF(E_LOG, L_PLAYER, "Source is not providing sufficient data, temporarily pausing playback\n");
|
||||
playback_abort(); // TODO Actually pause input + implement a resume event
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
DPRINTF(E_DBG, L_PLAYER, "Partial read (offset=%zu, pending=%d)\n", pb_buffer_offset, pb_writes_pending);
|
||||
DPRINTF(E_SPAM, L_PLAYER, "Partial read (offset=%zu, pending=%d)\n", pb_buffer_offset, pb_writes_pending);
|
||||
pb_buffer_offset += got;
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue