summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Pennequin <nicolas.pennequin@free.fr>2007-11-26 21:13:08 +0000
committerNicolas Pennequin <nicolas.pennequin@free.fr>2007-11-26 21:13:08 +0000
commite24454f8b30ed9ff386a1d7a7dc32d3be09b4f73 (patch)
tree98c59ab834d96399272315b815c9e4bd09e5849c
parentfa92f1b763723146e75d91b3efd0fa2833419a37 (diff)
downloadrockbox-e24454f8b30ed9ff386a1d7a7dc32d3be09b4f73.tar.gz
rockbox-e24454f8b30ed9ff386a1d7a7dc32d3be09b4f73.zip
Buffering callbacks rework. There is now one callback for all the events that can occur. Callbacks are now registred only once instead of being removed after having been called.
Fix FS#8092 by flushing the audio when a rebuffer is needed. Also add some comments here and there. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15816 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/buffering.c58
-rw-r--r--apps/buffering.h16
-rw-r--r--apps/playback.c32
3 files changed, 70 insertions, 36 deletions
diff --git a/apps/buffering.c b/apps/buffering.c
index 85bbe86cc3..624debc8e1 100644
--- a/apps/buffering.c
+++ b/apps/buffering.c
@@ -153,7 +153,7 @@ static struct mutex llist_mutex;
This is global so that move_handle and rm_handle can invalidate it. */
static struct memory_handle *cached_handle = NULL;
-static buffer_low_callback buffer_low_callback_funcs[MAX_BUF_CALLBACKS];
+static buffering_callback buffering_callback_funcs[MAX_BUF_CALLBACKS];
static int buffer_callback_count = 0;
static struct {
@@ -188,6 +188,9 @@ static struct event_queue buffering_queue;
static struct queue_sender_list buffering_queue_sender_list;
+static void call_buffering_callbacks(enum callback_event ev, int value);
+
+
/*
LINKED LIST MANAGEMENT
======================
@@ -330,6 +333,7 @@ static bool rm_handle(const struct memory_handle *h)
}
} else {
struct memory_handle *m = first_handle;
+ /* Find the previous handle */
while (m && m->next != h) {
m = m->next;
}
@@ -686,6 +690,8 @@ static void rebuffer_handle(int handle_id, size_t newpos)
if (!h)
return;
+ /* When seeking foward off of the buffer, if it is a short seek don't
+ rebuffer the whole track, just read enough to satisfy */
if (newpos > h->offset && newpos - h->offset < BUFFERING_DEFAULT_FILECHUNK)
{
LOGFQUEUE("buffering >| Q_BUFFER_HANDLE");
@@ -693,12 +699,22 @@ static void rebuffer_handle(int handle_id, size_t newpos)
h->ridx = h->data + newpos;
return;
}
-
+
h->offset = newpos;
+ /* Reset the handle to its new offset */
LOGFQUEUE("buffering >| Q_RESET_HANDLE");
queue_send(&buffering_queue, Q_RESET_HANDLE, handle_id);
+ /* There isn't enough space to rebuffer all of the track from its new
+ offset, so we ask the user to free some */
+ if (buffer_len - BUF_USED < h->filesize - newpos)
+ {
+ DEBUGF("rebuffer_handle: space is needed\n");
+ call_buffering_callbacks(EVENT_HANDLE_REBUFFER, handle_id);
+ }
+
+ /* Now we ask for a rebuffer */
LOGFQUEUE("buffering >| Q_BUFFER_HANDLE");
queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id);
@@ -737,7 +753,7 @@ static void shrink_handle(struct memory_handle *h)
h->type == TYPE_ATOMIC_AUDIO))
{
/* metadata handle: we can move all of it */
- size_t handle_distance =
+ size_t handle_distance =
RINGBUF_SUB((unsigned)((void *)h->next - (void*)buffer), h->data);
delta = handle_distance - h->available;
@@ -1130,8 +1146,8 @@ buf_handle_offset
buf_request_buffer_handle
buf_set_base_handle
buf_used
-register_buffer_low_callback
-unregister_buffer_low_callback
+register_buffering_callback
+unregister_buffering_callback
These functions are exported, to allow interaction with the buffer.
They take care of the content of the structs, and rely on the linked list
@@ -1180,50 +1196,48 @@ void buf_set_watermark(size_t bytes)
queue_post(&buffering_queue, Q_SET_WATERMARK, bytes);
}
-bool register_buffer_low_callback(buffer_low_callback func)
+bool register_buffering_callback(buffering_callback func)
{
int i;
if (buffer_callback_count >= MAX_BUF_CALLBACKS)
return false;
for (i = 0; i < MAX_BUF_CALLBACKS; i++)
{
- if (buffer_low_callback_funcs[i] == NULL)
+ if (buffering_callback_funcs[i] == NULL)
{
- buffer_low_callback_funcs[i] = func;
+ buffering_callback_funcs[i] = func;
buffer_callback_count++;
return true;
}
- else if (buffer_low_callback_funcs[i] == func)
+ else if (buffering_callback_funcs[i] == func)
return true;
}
return false;
}
-void unregister_buffer_low_callback(buffer_low_callback func)
+void unregister_buffering_callback(buffering_callback func)
{
int i;
for (i = 0; i < MAX_BUF_CALLBACKS; i++)
{
- if (buffer_low_callback_funcs[i] == func)
+ if (buffering_callback_funcs[i] == func)
{
- buffer_low_callback_funcs[i] = NULL;
+ buffering_callback_funcs[i] = NULL;
buffer_callback_count--;
}
}
return;
}
-static void call_buffer_low_callbacks(void)
+static void call_buffering_callbacks(enum callback_event ev, int value)
{
- logf("call_buffer_low_callbacks()");
+ logf("call_buffering_callbacks()");
int i;
for (i = 0; i < MAX_BUF_CALLBACKS; i++)
{
- if (buffer_low_callback_funcs[i])
+ if (buffering_callback_funcs[i])
{
- buffer_low_callback_funcs[i]();
- buffer_low_callback_funcs[i] = NULL;
- buffer_callback_count--;
+ buffering_callback_funcs[i](ev, value);
}
}
}
@@ -1259,7 +1273,7 @@ void buffering_thread(void)
LOGFQUEUE("buffering < Q_START_FILL");
/* Call buffer callbacks here because this is one of two ways
* to begin a full buffer fill */
- call_buffer_low_callbacks();
+ call_buffering_callbacks(EVENT_BUFFER_LOW, 0);
shrink_buffer();
queue_reply(&buffering_queue, 1);
filling |= buffer_handle((int)ev.data);
@@ -1315,7 +1329,7 @@ void buffering_thread(void)
/* If the buffer is low, call the callbacks to get new data */
if (num_handles > 0 && data_counters.useful <= conf_watermark)
- call_buffer_low_callbacks();
+ call_buffering_callbacks(EVENT_BUFFER_LOW, 0);
#if 0
/* TODO: This needs to be fixed to use the idle callback, disable it
@@ -1325,7 +1339,7 @@ void buffering_thread(void)
else if (ata_disk_is_active() && queue_empty(&buffering_queue))
{
if (num_handles > 0 && data_counters.useful <= high_watermark)
- call_buffer_low_callbacks();
+ call_buffering_callbacks(EVENT_BUFFER_LOW, 0);
if (data_counters.remaining > 0 && BUF_USED <= high_watermark)
{
@@ -1390,7 +1404,7 @@ bool buffering_reset(char *buf, size_t buflen)
base_handle_id = -1;
buffer_callback_count = 0;
- memset(buffer_low_callback_funcs, 0, sizeof(buffer_low_callback_funcs));
+ memset(buffering_callback_funcs, 0, sizeof(buffering_callback_funcs));
/* Set the high watermark as 75% full...or 25% empty :) */
#if MEM > 8
diff --git a/apps/buffering.h b/apps/buffering.h
index 06895e7e51..f6a9f7883b 100644
--- a/apps/buffering.h
+++ b/apps/buffering.h
@@ -35,6 +35,14 @@ enum data_type {
TYPE_UNKNOWN,
};
+enum callback_event {
+ EVENT_DEFAULT,
+ EVENT_BUFFER_LOW,
+ EVENT_HANDLE_REBUFFER,
+ EVENT_HANDLE_CLOSED,
+ EVENT_HANDLE_MOVED,
+};
+
/* Error return values */
#define ERR_HANDLE_NOT_FOUND -1
#define ERR_BUFFER_FULL -2
@@ -98,7 +106,7 @@ size_t buf_used(void);
* CALLBACK UTILITIES
* ==================
*
- * register_buffer_low_callback, unregister_buffer_low_callback:
+ * register_buffering_callback, unregister_buffering_callback:
*
* Register/Unregister callback functions that will get executed when the buffer
* goes below the low watermark. They are executed once, then forgotten.
@@ -108,9 +116,9 @@ size_t buf_used(void);
****************************************************************************/
#define MAX_BUF_CALLBACKS 4
-typedef void (*buffer_low_callback)(void);
-bool register_buffer_low_callback(buffer_low_callback func);
-void unregister_buffer_low_callback(buffer_low_callback func);
+typedef void (*buffering_callback)(enum callback_event ev, int value);
+bool register_buffering_callback(buffering_callback func);
+void unregister_buffering_callback(buffering_callback func);
/* Settings */
enum {
diff --git a/apps/playback.c b/apps/playback.c
index 5e42b7629d..a19c2d745a 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -1468,10 +1468,26 @@ static void audio_update_trackinfo(void)
ci.taginfo_ready = &CUR_TI->taginfo_ready;
}
-static void low_buffer_callback(void)
+static void buffering_audio_callback(enum callback_event ev, int value)
{
- LOGFQUEUE("buffering > audio Q_AUDIO_FILL_BUFFER");
- queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
+ (void)value;
+ logf("buffering_audio_callback");
+
+ switch (ev)
+ {
+ case EVENT_BUFFER_LOW:
+ LOGFQUEUE("buffering > audio Q_AUDIO_FILL_BUFFER");
+ queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
+ break;
+
+ case EVENT_HANDLE_REBUFFER:
+ LOGFQUEUE("audio >| audio Q_AUDIO_FLUSH");
+ queue_send(&audio_queue, Q_AUDIO_FLUSH, 0);
+ break;
+
+ default:
+ break;
+ }
}
/* Clear tracks between write and read, non inclusive */
@@ -1481,10 +1497,6 @@ static void audio_clear_track_entries(bool clear_unbuffered)
logf("Clearing tracks:%d/%d, %d", track_ridx, track_widx, clear_unbuffered);
- /* This function is always called in association with a stop or a rebuffer,
- * we will reregister the callback at the end of a rebuffer if needed */
- unregister_buffer_low_callback(low_buffer_callback);
-
/* Loop over all tracks from write-to-read */
while (1)
{
@@ -1919,9 +1931,6 @@ static void audio_fill_file_buffer(bool start_play, size_t offset)
track_changed = true;
audio_generate_postbuffer_events();
-
- if (!continue_buffering)
- register_buffer_low_callback(low_buffer_callback);
}
static void audio_rebuffer(void)
@@ -2179,6 +2188,8 @@ static void audio_stop_playback(void)
/* Close all tracks */
audio_release_tracks();
+ unregister_buffering_callback(buffering_audio_callback);
+
memset(&curtrack_id3, 0, sizeof(struct mp3entry));
}
@@ -2221,6 +2232,7 @@ static void audio_play_start(size_t offset)
set_filebuf_watermark(buffer_margin, 0);
#endif
audio_fill_file_buffer(true, offset);
+ register_buffering_callback(buffering_audio_callback);
LOGFQUEUE("audio > audio Q_AUDIO_TRACK_CHANGED");
queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);