mirror of
https://github.com/owntone/owntone-server.git
synced 2025-04-15 16:48:22 -04:00
[input] Fix pipe playback bringing cpu to 100%
When pipe playback is started, but no data is written to the pipe, the input loop would bring the cpu to 100%. This fix limits the loop like it was before player refactor.
This commit is contained in:
parent
ef0a194644
commit
ef9c0117c9
65
src/input.c
65
src/input.c
@ -44,8 +44,8 @@
|
|||||||
// Disallow further writes to the buffer when its size exceeds this threshold.
|
// Disallow further writes to the buffer when its size exceeds this threshold.
|
||||||
// The below gives us room to buffer 2 seconds of 48000/16/2 audio.
|
// The below gives us room to buffer 2 seconds of 48000/16/2 audio.
|
||||||
#define INPUT_BUFFER_THRESHOLD STOB(96000, 16, 2)
|
#define INPUT_BUFFER_THRESHOLD STOB(96000, 16, 2)
|
||||||
// How long (in sec) to wait for player read before looping
|
// How long (in nsec) to wait when the input buffer is full before looping
|
||||||
#define INPUT_LOOP_TIMEOUT 1
|
#define INPUT_LOOP_TIMEOUT_NSEC 10000000
|
||||||
// How long (in sec) to keep an input open without the player reading from it
|
// How long (in sec) to keep an input open without the player reading from it
|
||||||
#define INPUT_OPEN_TIMEOUT 600
|
#define INPUT_OPEN_TIMEOUT 600
|
||||||
|
|
||||||
@ -134,7 +134,7 @@ static struct input_source input_now_reading;
|
|||||||
static struct input_buffer input_buffer;
|
static struct input_buffer input_buffer;
|
||||||
|
|
||||||
// Timeout waiting in playback loop
|
// Timeout waiting in playback loop
|
||||||
static struct timespec input_loop_timeout = { INPUT_LOOP_TIMEOUT, 0 };
|
static struct timespec input_loop_timeout = { 0, INPUT_LOOP_TIMEOUT_NSEC };
|
||||||
|
|
||||||
// Timeout waiting for player read
|
// Timeout waiting for player read
|
||||||
static struct timeval input_open_timeout = { INPUT_OPEN_TIMEOUT, 0 };
|
static struct timeval input_open_timeout = { INPUT_OPEN_TIMEOUT, 0 };
|
||||||
@ -639,31 +639,13 @@ input_wait(void)
|
|||||||
|
|
||||||
pthread_mutex_lock(&input_buffer.mutex);
|
pthread_mutex_lock(&input_buffer.mutex);
|
||||||
|
|
||||||
// Is the buffer full? Then wait for a read or for loop_timeout to elapse
|
ts = timespec_reltoabs(input_loop_timeout);
|
||||||
if (evbuffer_get_length(input_buffer.evbuf) > INPUT_BUFFER_THRESHOLD)
|
pthread_cond_timedwait(&input_buffer.cond, &input_buffer.mutex, &ts);
|
||||||
{
|
|
||||||
buffer_full_cb();
|
|
||||||
|
|
||||||
ts = timespec_reltoabs(input_loop_timeout);
|
|
||||||
pthread_cond_timedwait(&input_buffer.cond, &input_buffer.mutex, &ts);
|
|
||||||
|
|
||||||
if (evbuffer_get_length(input_buffer.evbuf) > INPUT_BUFFER_THRESHOLD)
|
|
||||||
{
|
|
||||||
pthread_mutex_unlock(&input_buffer.mutex);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&input_buffer.mutex);
|
pthread_mutex_unlock(&input_buffer.mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*void
|
|
||||||
input_next(void)
|
|
||||||
{
|
|
||||||
commands_exec_async(cmdbase, next, NULL);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/* ---------------------------------- MAIN ---------------------------------- */
|
/* ---------------------------------- MAIN ---------------------------------- */
|
||||||
/* Thread: input */
|
/* Thread: input */
|
||||||
|
|
||||||
@ -694,6 +676,32 @@ input(void *arg)
|
|||||||
pthread_exit(NULL);
|
pthread_exit(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
wait_buffer_ready(void)
|
||||||
|
{
|
||||||
|
struct timespec ts;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&input_buffer.mutex);
|
||||||
|
|
||||||
|
// Is the buffer full? Then wait for a read or for loop_timeout to elapse
|
||||||
|
if (evbuffer_get_length(input_buffer.evbuf) > INPUT_BUFFER_THRESHOLD)
|
||||||
|
{
|
||||||
|
buffer_full_cb();
|
||||||
|
|
||||||
|
ts = timespec_reltoabs(input_loop_timeout);
|
||||||
|
pthread_cond_timedwait(&input_buffer.cond, &input_buffer.mutex, &ts);
|
||||||
|
|
||||||
|
if (evbuffer_get_length(input_buffer.evbuf) > INPUT_BUFFER_THRESHOLD)
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&input_buffer.mutex);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&input_buffer.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
play(evutil_socket_t fd, short flags, void *arg)
|
play(evutil_socket_t fd, short flags, void *arg)
|
||||||
{
|
{
|
||||||
@ -705,6 +713,17 @@ play(evutil_socket_t fd, short flags, void *arg)
|
|||||||
if (!inputs[input_now_reading.type]->play)
|
if (!inputs[input_now_reading.type]->play)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// If the buffer is full we wait until either the player has consumed enough
|
||||||
|
// data or INPUT_LOOP_TIMEOUT has elapsed (so we don't hang the input event
|
||||||
|
// thread when the player doesn't consume data quickly). If the return is
|
||||||
|
// negative then the buffer is still full, so we loop.
|
||||||
|
ret = wait_buffer_ready();
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
event_add(input_ev, &tv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Return will be negative if there is an error or EOF. Here, we just don't
|
// Return will be negative if there is an error or EOF. Here, we just don't
|
||||||
// loop any more. input_write() will pass the message to the player.
|
// loop any more. input_write() will pass the message to the player.
|
||||||
ret = inputs[input_now_reading.type]->play(&input_now_reading);
|
ret = inputs[input_now_reading.type]->play(&input_now_reading);
|
||||||
|
13
src/input.h
13
src/input.h
@ -143,21 +143,12 @@ int
|
|||||||
input_write(struct evbuffer *evbuf, struct media_quality *quality, short flags);
|
input_write(struct evbuffer *evbuf, struct media_quality *quality, short flags);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Input modules can use this to wait for the input_buffer to be ready for
|
* Input modules can use this to wait for the player to read, so the module's
|
||||||
* writing. The wait is max INPUT_LOOP_TIMEOUT, which allows the event base to
|
* playback-loop doesn't spin out of control.
|
||||||
* loop and process pending commands once in a while.
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
input_wait(void);
|
input_wait(void);
|
||||||
|
|
||||||
/*
|
|
||||||
* Async switch to the next song in the queue. Mostly for internal use, but
|
|
||||||
* might be relevant some day externally?
|
|
||||||
*/
|
|
||||||
//void
|
|
||||||
//input_next(void);
|
|
||||||
|
|
||||||
|
|
||||||
/* ---------------------- Interface towards player thread ------------------- */
|
/* ---------------------- Interface towards player thread ------------------- */
|
||||||
/* Thread: player */
|
/* Thread: player */
|
||||||
|
|
||||||
|
@ -87,10 +87,6 @@ play(struct input_source *source)
|
|||||||
int ret;
|
int ret;
|
||||||
short flags;
|
short flags;
|
||||||
|
|
||||||
ret = input_wait();
|
|
||||||
if (ret < 0)
|
|
||||||
return 0; // Loop, input_buffer is not ready for writing
|
|
||||||
|
|
||||||
// We set "wanted" to 1 because the read size doesn't matter to us
|
// We set "wanted" to 1 because the read size doesn't matter to us
|
||||||
// TODO optimize?
|
// TODO optimize?
|
||||||
ret = transcode(source->evbuf, &icy_timer, ctx, 1);
|
ret = transcode(source->evbuf, &icy_timer, ctx, 1);
|
||||||
|
@ -872,10 +872,6 @@ play(struct input_source *source)
|
|||||||
short flags;
|
short flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = input_wait();
|
|
||||||
if (ret < 0)
|
|
||||||
return 0; // Loop, input_buffer is not ready for writing
|
|
||||||
|
|
||||||
ret = evbuffer_read(source->evbuf, pipe->fd, PIPE_READ_MAX);
|
ret = evbuffer_read(source->evbuf, pipe->fd, PIPE_READ_MAX);
|
||||||
if ((ret == 0) && (pipe->is_autostarted))
|
if ((ret == 0) && (pipe->is_autostarted))
|
||||||
{
|
{
|
||||||
@ -885,6 +881,7 @@ play(struct input_source *source)
|
|||||||
}
|
}
|
||||||
else if ((ret == 0) || ((ret < 0) && (errno == EAGAIN)))
|
else if ((ret == 0) || ((ret < 0) && (errno == EAGAIN)))
|
||||||
{
|
{
|
||||||
|
input_wait();
|
||||||
return 0; // Loop
|
return 0; // Loop
|
||||||
}
|
}
|
||||||
else if (ret < 0)
|
else if (ret < 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user