diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-04-27 03:08:23 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-04-27 03:08:23 +0000 |
commit | c537d5958e8b421ac4f9bef6c8b9e7425a6cf167 (patch) | |
tree | 7ed36518fb6524da7bbd913ba7619b85b5d15d23 /apps/buffering.c | |
parent | dcf0f8de4a37ff1d2ea510aef75fa67977a8bdcc (diff) | |
download | rockbox-c537d5958e8b421ac4f9bef6c8b9e7425a6cf167.tar.gz rockbox-c537d5958e8b421ac4f9bef6c8b9e7425a6cf167.zip |
Commit FS#12069 - Playback rework - first stages. Gives as thorough as possible a treatment of codec management, track change and metadata logic as possible while maintaining fairly narrow focus and not rewriting everything all at once. Please see the rockbox-dev mail archive on 2011-04-25 (Playback engine rework) for a more thorough manifest of what was addressed. Plugins and codecs become incompatible.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29785 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/buffering.c')
-rw-r--r-- | apps/buffering.c | 288 |
1 files changed, 204 insertions, 84 deletions
diff --git a/apps/buffering.c b/apps/buffering.c index 578f0f261a..a130a787ff 100644 --- a/apps/buffering.c +++ b/apps/buffering.c | |||
@@ -58,7 +58,7 @@ | |||
58 | #define GUARD_BUFSIZE (32*1024) | 58 | #define GUARD_BUFSIZE (32*1024) |
59 | 59 | ||
60 | /* Define LOGF_ENABLE to enable logf output in this file */ | 60 | /* Define LOGF_ENABLE to enable logf output in this file */ |
61 | /*#define LOGF_ENABLE*/ | 61 | /* #define LOGF_ENABLE */ |
62 | #include "logf.h" | 62 | #include "logf.h" |
63 | 63 | ||
64 | /* macros to enable logf for queues | 64 | /* macros to enable logf for queues |
@@ -82,8 +82,6 @@ | |||
82 | #define LOGFQUEUE_SYS_TIMEOUT(...) | 82 | #define LOGFQUEUE_SYS_TIMEOUT(...) |
83 | #endif | 83 | #endif |
84 | 84 | ||
85 | /* default point to start buffer refill */ | ||
86 | #define BUFFERING_DEFAULT_WATERMARK (1024*128) | ||
87 | /* amount of data to read in one read() call */ | 85 | /* amount of data to read in one read() call */ |
88 | #define BUFFERING_DEFAULT_FILECHUNK (1024*32) | 86 | #define BUFFERING_DEFAULT_FILECHUNK (1024*32) |
89 | 87 | ||
@@ -94,6 +92,8 @@ | |||
94 | struct memory_handle { | 92 | struct memory_handle { |
95 | int id; /* A unique ID for the handle */ | 93 | int id; /* A unique ID for the handle */ |
96 | enum data_type type; /* Type of data buffered with this handle */ | 94 | enum data_type type; /* Type of data buffered with this handle */ |
95 | int8_t pinned; /* Count of references */ | ||
96 | int8_t signaled; /* Stop any attempt at waiting to get the data */ | ||
97 | char path[MAX_PATH]; /* Path if data originated in a file */ | 97 | char path[MAX_PATH]; /* Path if data originated in a file */ |
98 | int fd; /* File descriptor to path (-1 if closed) */ | 98 | int fd; /* File descriptor to path (-1 if closed) */ |
99 | size_t data; /* Start index of the handle's data buffer */ | 99 | size_t data; /* Start index of the handle's data buffer */ |
@@ -125,9 +125,7 @@ static volatile size_t buf_ridx; /* current reading position */ | |||
125 | 125 | ||
126 | /* Configuration */ | 126 | /* Configuration */ |
127 | static size_t conf_watermark = 0; /* Level to trigger filebuf fill */ | 127 | static size_t conf_watermark = 0; /* Level to trigger filebuf fill */ |
128 | #if MEMORYSIZE > 8 | ||
129 | static size_t high_watermark = 0; /* High watermark for rebuffer */ | 128 | static size_t high_watermark = 0; /* High watermark for rebuffer */ |
130 | #endif | ||
131 | 129 | ||
132 | /* current memory handle in the linked list. NULL when the list is empty. */ | 130 | /* current memory handle in the linked list. NULL when the list is empty. */ |
133 | static struct memory_handle *cur_handle; | 131 | static struct memory_handle *cur_handle; |
@@ -162,7 +160,6 @@ enum | |||
162 | Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new | 160 | Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new |
163 | file starting position. */ | 161 | file starting position. */ |
164 | Q_CLOSE_HANDLE, /* Request closing a handle */ | 162 | Q_CLOSE_HANDLE, /* Request closing a handle */ |
165 | Q_BASE_HANDLE, /* Set the reference handle for buf_useful_data */ | ||
166 | 163 | ||
167 | /* Configuration: */ | 164 | /* Configuration: */ |
168 | Q_START_FILL, /* Request that the buffering thread initiate a buffer | 165 | Q_START_FILL, /* Request that the buffering thread initiate a buffer |
@@ -222,6 +219,9 @@ static inline ssize_t ringbuf_add_cross(uintptr_t p1, size_t v, uintptr_t p2) | |||
222 | /* Bytes available in the buffer */ | 219 | /* Bytes available in the buffer */ |
223 | #define BUF_USED ringbuf_sub(buf_widx, buf_ridx) | 220 | #define BUF_USED ringbuf_sub(buf_widx, buf_ridx) |
224 | 221 | ||
222 | /* Real buffer watermark */ | ||
223 | #define BUF_WATERMARK MIN(conf_watermark, high_watermark) | ||
224 | |||
225 | /* | 225 | /* |
226 | LINKED LIST MANAGEMENT | 226 | LINKED LIST MANAGEMENT |
227 | ====================== | 227 | ====================== |
@@ -313,6 +313,12 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, | |||
313 | /* Prevent buffering thread from looking at it */ | 313 | /* Prevent buffering thread from looking at it */ |
314 | new_handle->filerem = 0; | 314 | new_handle->filerem = 0; |
315 | 315 | ||
316 | /* Handle can be moved by default */ | ||
317 | new_handle->pinned = 0; | ||
318 | |||
319 | /* Handle data can be waited for by default */ | ||
320 | new_handle->signaled = 0; | ||
321 | |||
316 | /* only advance the buffer write index of the size of the struct */ | 322 | /* only advance the buffer write index of the size of the struct */ |
317 | buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle)); | 323 | buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle)); |
318 | 324 | ||
@@ -364,6 +370,9 @@ static bool rm_handle(const struct memory_handle *h) | |||
364 | buf_widx = cur_handle->widx; | 370 | buf_widx = cur_handle->widx; |
365 | } | 371 | } |
366 | } else { | 372 | } else { |
373 | /* If we don't find ourselves, this is a seriously incoherent | ||
374 | state with a corrupted list and severe action is needed! */ | ||
375 | panicf("rm_handle fail: %d", h->id); | ||
367 | return false; | 376 | return false; |
368 | } | 377 | } |
369 | } | 378 | } |
@@ -385,8 +394,7 @@ static struct memory_handle *find_handle(int handle_id) | |||
385 | 394 | ||
386 | /* simple caching because most of the time the requested handle | 395 | /* simple caching because most of the time the requested handle |
387 | will either be the same as the last, or the one after the last */ | 396 | will either be the same as the last, or the one after the last */ |
388 | if (cached_handle) | 397 | if (cached_handle) { |
389 | { | ||
390 | if (cached_handle->id == handle_id) { | 398 | if (cached_handle->id == handle_id) { |
391 | return cached_handle; | 399 | return cached_handle; |
392 | } else if (cached_handle->next && | 400 | } else if (cached_handle->next && |
@@ -618,20 +626,22 @@ static void update_data_counters(struct data_counters *dc) | |||
618 | static inline bool buffer_is_low(void) | 626 | static inline bool buffer_is_low(void) |
619 | { | 627 | { |
620 | update_data_counters(NULL); | 628 | update_data_counters(NULL); |
621 | return data_counters.useful < (conf_watermark / 2); | 629 | return data_counters.useful < BUF_WATERMARK / 2; |
622 | } | 630 | } |
623 | 631 | ||
624 | /* Q_BUFFER_HANDLE event and buffer data for the given handle. | 632 | /* Q_BUFFER_HANDLE event and buffer data for the given handle. |
625 | Return whether or not the buffering should continue explicitly. */ | 633 | Return whether or not the buffering should continue explicitly. */ |
626 | static bool buffer_handle(int handle_id, size_t to_buffer) | 634 | static bool buffer_handle(int handle_id, size_t to_buffer) |
627 | { | 635 | { |
628 | logf("buffer_handle(%d)", handle_id); | 636 | logf("buffer_handle(%d, %lu)", handle_id, (unsigned long)to_buffer); |
629 | struct memory_handle *h = find_handle(handle_id); | 637 | struct memory_handle *h = find_handle(handle_id); |
630 | bool stop = false; | 638 | bool stop = false; |
631 | 639 | ||
632 | if (!h) | 640 | if (!h) |
633 | return true; | 641 | return true; |
634 | 642 | ||
643 | logf(" type: %d", (int)h->type); | ||
644 | |||
635 | if (h->filerem == 0) { | 645 | if (h->filerem == 0) { |
636 | /* nothing left to buffer */ | 646 | /* nothing left to buffer */ |
637 | return true; | 647 | return true; |
@@ -659,13 +669,13 @@ static bool buffer_handle(int handle_id, size_t to_buffer) | |||
659 | if (!get_metadata((struct mp3entry *)(buffer + h->data), | 669 | if (!get_metadata((struct mp3entry *)(buffer + h->data), |
660 | h->fd, h->path)) { | 670 | h->fd, h->path)) { |
661 | /* metadata parsing failed: clear the buffer. */ | 671 | /* metadata parsing failed: clear the buffer. */ |
662 | memset(buffer + h->data, 0, sizeof(struct mp3entry)); | 672 | wipe_mp3entry((struct mp3entry *)(buffer + h->data)); |
663 | } | 673 | } |
664 | close(h->fd); | 674 | close(h->fd); |
665 | h->fd = -1; | 675 | h->fd = -1; |
666 | h->filerem = 0; | 676 | h->filerem = 0; |
667 | h->available = sizeof(struct mp3entry); | 677 | h->available = sizeof(struct mp3entry); |
668 | h->widx += sizeof(struct mp3entry); | 678 | h->widx = ringbuf_add(h->widx, sizeof(struct mp3entry)); |
669 | send_event(BUFFER_EVENT_FINISHED, &handle_id); | 679 | send_event(BUFFER_EVENT_FINISHED, &handle_id); |
670 | return true; | 680 | return true; |
671 | } | 681 | } |
@@ -698,7 +708,7 @@ static bool buffer_handle(int handle_id, size_t to_buffer) | |||
698 | break; | 708 | break; |
699 | } | 709 | } |
700 | 710 | ||
701 | DEBUGF("File ended %ld bytes early\n", (long)h->filerem); | 711 | logf("File ended %ld bytes early\n", (long)h->filerem); |
702 | h->filesize -= h->filerem; | 712 | h->filesize -= h->filerem; |
703 | h->filerem = 0; | 713 | h->filerem = 0; |
704 | break; | 714 | break; |
@@ -770,22 +780,31 @@ static bool close_handle(int handle_id) | |||
770 | part of its data buffer or by moving all the data. */ | 780 | part of its data buffer or by moving all the data. */ |
771 | static void shrink_handle(struct memory_handle *h) | 781 | static void shrink_handle(struct memory_handle *h) |
772 | { | 782 | { |
773 | size_t delta; | ||
774 | |||
775 | if (!h) | 783 | if (!h) |
776 | return; | 784 | return; |
777 | 785 | ||
778 | if (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET || | 786 | if (h->type == TYPE_PACKET_AUDIO) { |
779 | h->type == TYPE_BITMAP || h->type == TYPE_CODEC || | 787 | /* only move the handle struct */ |
780 | h->type == TYPE_ATOMIC_AUDIO) | 788 | /* data is pinned by default - if we start moving packet audio, |
781 | { | 789 | the semantics will determine whether or not data is movable |
790 | but the handle will remain movable in either case */ | ||
791 | size_t delta = ringbuf_sub(h->ridx, h->data); | ||
792 | |||
793 | /* The value of delta might change for alignment reasons */ | ||
794 | if (!move_handle(&h, &delta, 0, true)) | ||
795 | return; | ||
796 | |||
797 | h->data = ringbuf_add(h->data, delta); | ||
798 | h->available -= delta; | ||
799 | h->offset += delta; | ||
800 | } else { | ||
782 | /* metadata handle: we can move all of it */ | 801 | /* metadata handle: we can move all of it */ |
783 | if (!h->next || h->filerem != 0) | 802 | if (h->pinned || !h->next || h->filerem != 0) |
784 | return; /* Last handle or not finished loading */ | 803 | return; /* Pinned, last handle or not finished loading */ |
785 | 804 | ||
786 | uintptr_t handle_distance = | 805 | uintptr_t handle_distance = |
787 | ringbuf_sub(ringbuf_offset(h->next), h->data); | 806 | ringbuf_sub(ringbuf_offset(h->next), h->data); |
788 | delta = handle_distance - h->available; | 807 | size_t delta = handle_distance - h->available; |
789 | 808 | ||
790 | /* The value of delta might change for alignment reasons */ | 809 | /* The value of delta might change for alignment reasons */ |
791 | if (!move_handle(&h, &delta, h->available, h->type==TYPE_CODEC)) | 810 | if (!move_handle(&h, &delta, h->available, h->type==TYPE_CODEC)) |
@@ -806,15 +825,6 @@ static void shrink_handle(struct memory_handle *h) | |||
806 | struct bitmap *bmp = (struct bitmap *)&buffer[h->data]; | 825 | struct bitmap *bmp = (struct bitmap *)&buffer[h->data]; |
807 | bmp->data = &buffer[h->data + sizeof(struct bitmap)]; | 826 | bmp->data = &buffer[h->data + sizeof(struct bitmap)]; |
808 | } | 827 | } |
809 | } else { | ||
810 | /* only move the handle struct */ | ||
811 | delta = ringbuf_sub(h->ridx, h->data); | ||
812 | if (!move_handle(&h, &delta, 0, true)) | ||
813 | return; | ||
814 | |||
815 | h->data = ringbuf_add(h->data, delta); | ||
816 | h->available -= delta; | ||
817 | h->offset += delta; | ||
818 | } | 828 | } |
819 | } | 829 | } |
820 | 830 | ||
@@ -962,6 +972,8 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
962 | mutex_unlock(&llist_mutex); | 972 | mutex_unlock(&llist_mutex); |
963 | return handle_id; | 973 | return handle_id; |
964 | } | 974 | } |
975 | else if (type == TYPE_UNKNOWN) | ||
976 | return ERR_UNSUPPORTED_TYPE; | ||
965 | #ifdef APPLICATION | 977 | #ifdef APPLICATION |
966 | /* loading code from memory is not supported in application builds */ | 978 | /* loading code from memory is not supported in application builds */ |
967 | else if (type == TYPE_CODEC) | 979 | else if (type == TYPE_CODEC) |
@@ -1083,7 +1095,12 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
1083 | */ | 1095 | */ |
1084 | int bufalloc(const void *src, size_t size, enum data_type type) | 1096 | int bufalloc(const void *src, size_t size, enum data_type type) |
1085 | { | 1097 | { |
1086 | int handle_id = ERR_BUFFER_FULL; | 1098 | int handle_id; |
1099 | |||
1100 | if (type == TYPE_UNKNOWN) | ||
1101 | return ERR_UNSUPPORTED_TYPE; | ||
1102 | |||
1103 | handle_id = ERR_BUFFER_FULL; | ||
1087 | 1104 | ||
1088 | mutex_lock(&llist_mutex); | 1105 | mutex_lock(&llist_mutex); |
1089 | 1106 | ||
@@ -1124,7 +1141,14 @@ int bufalloc(const void *src, size_t size, enum data_type type) | |||
1124 | bool bufclose(int handle_id) | 1141 | bool bufclose(int handle_id) |
1125 | { | 1142 | { |
1126 | logf("bufclose(%d)", handle_id); | 1143 | logf("bufclose(%d)", handle_id); |
1127 | 1144 | #if 0 | |
1145 | /* Don't interrupt the buffering thread if the handle is already | ||
1146 | stale */ | ||
1147 | if (!find_handle(handle_id)) { | ||
1148 | logf(" handle already closed"); | ||
1149 | return true; | ||
1150 | } | ||
1151 | #endif | ||
1128 | LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id); | 1152 | LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id); |
1129 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); | 1153 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); |
1130 | } | 1154 | } |
@@ -1236,9 +1260,10 @@ static int seek_handle(struct memory_handle *h, size_t newpos) | |||
1236 | 1260 | ||
1237 | /* Set reading index in handle (relatively to the start of the file). | 1261 | /* Set reading index in handle (relatively to the start of the file). |
1238 | Access before the available data will trigger a rebuffer. | 1262 | Access before the available data will trigger a rebuffer. |
1239 | Return 0 for success and < 0 for failure: | 1263 | Return 0 for success and for failure: |
1240 | -1 if the handle wasn't found | 1264 | ERR_HANDLE_NOT_FOUND if the handle wasn't found |
1241 | -2 if the new requested position was beyond the end of the file | 1265 | ERR_INVALID_VALUE if the new requested position was beyond the end of |
1266 | the file | ||
1242 | */ | 1267 | */ |
1243 | int bufseek(int handle_id, size_t newpos) | 1268 | int bufseek(int handle_id, size_t newpos) |
1244 | { | 1269 | { |
@@ -1250,7 +1275,11 @@ int bufseek(int handle_id, size_t newpos) | |||
1250 | } | 1275 | } |
1251 | 1276 | ||
1252 | /* Advance the reading index in a handle (relatively to its current position). | 1277 | /* Advance the reading index in a handle (relatively to its current position). |
1253 | Return 0 for success and < 0 for failure */ | 1278 | Return 0 for success and for failure: |
1279 | ERR_HANDLE_NOT_FOUND if the handle wasn't found | ||
1280 | ERR_INVALID_VALUE if the new requested position was beyond the end of | ||
1281 | the file | ||
1282 | */ | ||
1254 | int bufadvance(int handle_id, off_t offset) | 1283 | int bufadvance(int handle_id, off_t offset) |
1255 | { | 1284 | { |
1256 | struct memory_handle *h = find_handle(handle_id); | 1285 | struct memory_handle *h = find_handle(handle_id); |
@@ -1261,6 +1290,18 @@ int bufadvance(int handle_id, off_t offset) | |||
1261 | return seek_handle(h, newpos); | 1290 | return seek_handle(h, newpos); |
1262 | } | 1291 | } |
1263 | 1292 | ||
1293 | /* Get the read position from the start of the file | ||
1294 | Returns the offset from byte 0 of the file and for failure: | ||
1295 | ERR_HANDLE_NOT_FOUND if the handle wasn't found | ||
1296 | */ | ||
1297 | off_t bufftell(int handle_id) | ||
1298 | { | ||
1299 | const struct memory_handle *h = find_handle(handle_id); | ||
1300 | if (!h) | ||
1301 | return ERR_HANDLE_NOT_FOUND; | ||
1302 | return h->offset + ringbuf_sub(h->ridx, h->data); | ||
1303 | } | ||
1304 | |||
1264 | /* Used by bufread and bufgetdata to prepare the buffer and retrieve the | 1305 | /* Used by bufread and bufgetdata to prepare the buffer and retrieve the |
1265 | * actual amount of data available for reading. This function explicitly | 1306 | * actual amount of data available for reading. This function explicitly |
1266 | * does not check the validity of the input handle. It does do range checks | 1307 | * does not check the validity of the input handle. It does do range checks |
@@ -1306,7 +1347,7 @@ static struct memory_handle *prep_bufdata(int handle_id, size_t *size, | |||
1306 | /* it is not safe for a non-buffering thread to sleep while | 1347 | /* it is not safe for a non-buffering thread to sleep while |
1307 | * holding a handle */ | 1348 | * holding a handle */ |
1308 | h = find_handle(handle_id); | 1349 | h = find_handle(handle_id); |
1309 | if (!h) | 1350 | if (!h || h->signaled != 0) |
1310 | return NULL; | 1351 | return NULL; |
1311 | avail = handle_size_available(h); | 1352 | avail = handle_size_available(h); |
1312 | } | 1353 | } |
@@ -1447,9 +1488,14 @@ SECONDARY EXPORTED FUNCTIONS | |||
1447 | buf_handle_offset | 1488 | buf_handle_offset |
1448 | buf_request_buffer_handle | 1489 | buf_request_buffer_handle |
1449 | buf_set_base_handle | 1490 | buf_set_base_handle |
1491 | buf_handle_data_type | ||
1492 | buf_is_handle | ||
1493 | buf_pin_handle | ||
1494 | buf_signal_handle | ||
1495 | buf_length | ||
1450 | buf_used | 1496 | buf_used |
1451 | register_buffering_callback | 1497 | buf_set_watermark |
1452 | unregister_buffering_callback | 1498 | buf_get_watermark |
1453 | 1499 | ||
1454 | These functions are exported, to allow interaction with the buffer. | 1500 | These functions are exported, to allow interaction with the buffer. |
1455 | They take care of the content of the structs, and rely on the linked list | 1501 | They take care of the content of the structs, and rely on the linked list |
@@ -1472,8 +1518,61 @@ void buf_request_buffer_handle(int handle_id) | |||
1472 | 1518 | ||
1473 | void buf_set_base_handle(int handle_id) | 1519 | void buf_set_base_handle(int handle_id) |
1474 | { | 1520 | { |
1475 | LOGFQUEUE("buffering > Q_BASE_HANDLE %d", handle_id); | 1521 | mutex_lock(&llist_mutex); |
1476 | queue_post(&buffering_queue, Q_BASE_HANDLE, handle_id); | 1522 | base_handle_id = handle_id; |
1523 | mutex_unlock(&llist_mutex); | ||
1524 | } | ||
1525 | |||
1526 | enum data_type buf_handle_data_type(int handle_id) | ||
1527 | { | ||
1528 | const struct memory_handle *h = find_handle(handle_id); | ||
1529 | if (!h) | ||
1530 | return TYPE_UNKNOWN; | ||
1531 | return h->type; | ||
1532 | } | ||
1533 | |||
1534 | ssize_t buf_handle_remaining(int handle_id) | ||
1535 | { | ||
1536 | const struct memory_handle *h = find_handle(handle_id); | ||
1537 | if (!h) | ||
1538 | return ERR_HANDLE_NOT_FOUND; | ||
1539 | return h->filerem; | ||
1540 | } | ||
1541 | |||
1542 | bool buf_is_handle(int handle_id) | ||
1543 | { | ||
1544 | return find_handle(handle_id) != NULL; | ||
1545 | } | ||
1546 | |||
1547 | bool buf_pin_handle(int handle_id, bool pin) | ||
1548 | { | ||
1549 | struct memory_handle *h = find_handle(handle_id); | ||
1550 | if (!h) | ||
1551 | return false; | ||
1552 | |||
1553 | if (pin) { | ||
1554 | h->pinned++; | ||
1555 | } else if (h->pinned > 0) { | ||
1556 | h->pinned--; | ||
1557 | } | ||
1558 | |||
1559 | return true; | ||
1560 | } | ||
1561 | |||
1562 | bool buf_signal_handle(int handle_id, bool signal) | ||
1563 | { | ||
1564 | struct memory_handle *h = find_handle(handle_id); | ||
1565 | if (!h) | ||
1566 | return false; | ||
1567 | |||
1568 | h->signaled = signal ? 1 : 0; | ||
1569 | return true; | ||
1570 | } | ||
1571 | |||
1572 | /* Return the size of the ringbuffer */ | ||
1573 | size_t buf_length(void) | ||
1574 | { | ||
1575 | return buffer_len; | ||
1477 | } | 1576 | } |
1478 | 1577 | ||
1479 | /* Return the amount of buffer space used */ | 1578 | /* Return the amount of buffer space used */ |
@@ -1487,6 +1586,21 @@ void buf_set_watermark(size_t bytes) | |||
1487 | conf_watermark = bytes; | 1586 | conf_watermark = bytes; |
1488 | } | 1587 | } |
1489 | 1588 | ||
1589 | size_t buf_get_watermark(void) | ||
1590 | { | ||
1591 | return BUF_WATERMARK; | ||
1592 | } | ||
1593 | |||
1594 | #ifdef HAVE_IO_PRIORITY | ||
1595 | void buf_back_off_storage(bool back_off) | ||
1596 | { | ||
1597 | int priority = back_off ? | ||
1598 | IO_PRIORITY_BACKGROUND : IO_PRIORITY_IMMEDIATE; | ||
1599 | thread_set_io_priority(buffering_thread_id, priority); | ||
1600 | } | ||
1601 | #endif | ||
1602 | |||
1603 | /** -- buffer thread helpers -- **/ | ||
1490 | static void shrink_buffer_inner(struct memory_handle *h) | 1604 | static void shrink_buffer_inner(struct memory_handle *h) |
1491 | { | 1605 | { |
1492 | if (h == NULL) | 1606 | if (h == NULL) |
@@ -1503,7 +1617,7 @@ static void shrink_buffer(void) | |||
1503 | shrink_buffer_inner(first_handle); | 1617 | shrink_buffer_inner(first_handle); |
1504 | } | 1618 | } |
1505 | 1619 | ||
1506 | void buffering_thread(void) | 1620 | static void NORETURN_ATTR buffering_thread(void) |
1507 | { | 1621 | { |
1508 | bool filling = false; | 1622 | bool filling = false; |
1509 | struct queue_event ev; | 1623 | struct queue_event ev; |
@@ -1511,19 +1625,21 @@ void buffering_thread(void) | |||
1511 | 1625 | ||
1512 | while (true) | 1626 | while (true) |
1513 | { | 1627 | { |
1514 | if (!filling) { | 1628 | if (num_handles > 0) { |
1629 | if (!filling) { | ||
1630 | cancel_cpu_boost(); | ||
1631 | } | ||
1632 | queue_wait_w_tmo(&buffering_queue, &ev, filling ? 1 : HZ/2); | ||
1633 | } else { | ||
1634 | filling = false; | ||
1515 | cancel_cpu_boost(); | 1635 | cancel_cpu_boost(); |
1636 | queue_wait(&buffering_queue, &ev); | ||
1516 | } | 1637 | } |
1517 | 1638 | ||
1518 | queue_wait_w_tmo(&buffering_queue, &ev, filling ? 5 : HZ/2); | ||
1519 | |||
1520 | switch (ev.id) | 1639 | switch (ev.id) |
1521 | { | 1640 | { |
1522 | case Q_START_FILL: | 1641 | case Q_START_FILL: |
1523 | LOGFQUEUE("buffering < Q_START_FILL %d", (int)ev.data); | 1642 | LOGFQUEUE("buffering < Q_START_FILL %d", (int)ev.data); |
1524 | /* Call buffer callbacks here because this is one of two ways | ||
1525 | * to begin a full buffer fill */ | ||
1526 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); | ||
1527 | shrink_buffer(); | 1643 | shrink_buffer(); |
1528 | queue_reply(&buffering_queue, 1); | 1644 | queue_reply(&buffering_queue, 1); |
1529 | filling |= buffer_handle((int)ev.data, 0); | 1645 | filling |= buffer_handle((int)ev.data, 0); |
@@ -1553,36 +1669,21 @@ void buffering_thread(void) | |||
1553 | filling = true; | 1669 | filling = true; |
1554 | break; | 1670 | break; |
1555 | 1671 | ||
1556 | case Q_BASE_HANDLE: | ||
1557 | LOGFQUEUE("buffering < Q_BASE_HANDLE %d", (int)ev.data); | ||
1558 | base_handle_id = (int)ev.data; | ||
1559 | break; | ||
1560 | |||
1561 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
1562 | case SYS_USB_CONNECTED: | ||
1563 | LOGFQUEUE("buffering < SYS_USB_CONNECTED"); | ||
1564 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
1565 | usb_wait_for_disconnect(&buffering_queue); | ||
1566 | break; | ||
1567 | #endif | ||
1568 | |||
1569 | case SYS_TIMEOUT: | 1672 | case SYS_TIMEOUT: |
1570 | LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT"); | 1673 | LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT"); |
1571 | break; | 1674 | break; |
1572 | } | 1675 | } |
1573 | 1676 | ||
1574 | update_data_counters(NULL); | 1677 | if (num_handles == 0 || !queue_empty(&buffering_queue)) |
1575 | 1678 | continue; | |
1576 | /* If the buffer is low, call the callbacks to get new data */ | ||
1577 | if (num_handles > 0 && data_counters.useful <= conf_watermark) | ||
1578 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); | ||
1579 | 1679 | ||
1680 | update_data_counters(NULL); | ||
1580 | #if 0 | 1681 | #if 0 |
1581 | /* TODO: This needs to be fixed to use the idle callback, disable it | 1682 | /* TODO: This needs to be fixed to use the idle callback, disable it |
1582 | * for simplicity until its done right */ | 1683 | * for simplicity until its done right */ |
1583 | #if MEMORYSIZE > 8 | 1684 | #if MEMORYSIZE > 8 |
1584 | /* If the disk is spinning, take advantage by filling the buffer */ | 1685 | /* If the disk is spinning, take advantage by filling the buffer */ |
1585 | else if (storage_disk_is_active() && queue_empty(&buffering_queue)) { | 1686 | else if (storage_disk_is_active()) { |
1586 | if (num_handles > 0 && data_counters.useful <= high_watermark) | 1687 | if (num_handles > 0 && data_counters.useful <= high_watermark) |
1587 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); | 1688 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); |
1588 | 1689 | ||
@@ -1597,15 +1698,23 @@ void buffering_thread(void) | |||
1597 | #endif | 1698 | #endif |
1598 | #endif | 1699 | #endif |
1599 | 1700 | ||
1600 | if (queue_empty(&buffering_queue)) { | 1701 | if (filling) { |
1601 | if (filling) { | 1702 | if (data_counters.remaining > 0 && BUF_USED < buffer_len) { |
1602 | if (data_counters.remaining > 0 && BUF_USED < buffer_len) | 1703 | filling = fill_buffer(); |
1603 | filling = fill_buffer(); | 1704 | } |
1604 | else if (data_counters.remaining == 0) | 1705 | else if (data_counters.remaining == 0) { |
1605 | filling = false; | 1706 | filling = false; |
1606 | } else if (ev.id == SYS_TIMEOUT) { | 1707 | } |
1607 | if (data_counters.remaining > 0 && | 1708 | } else if (ev.id == SYS_TIMEOUT) { |
1608 | data_counters.useful <= conf_watermark) { | 1709 | if (data_counters.useful < BUF_WATERMARK) { |
1710 | /* The buffer is low and we're idle, just watching the levels | ||
1711 | - call the callbacks to get new data */ | ||
1712 | send_event(BUFFER_EVENT_BUFFER_LOW, NULL); | ||
1713 | |||
1714 | /* Continue anything else we haven't finished - it might | ||
1715 | get booted off or stop early because the receiver hasn't | ||
1716 | had a chance to clear anything yet */ | ||
1717 | if (data_counters.remaining > 0) { | ||
1609 | shrink_buffer(); | 1718 | shrink_buffer(); |
1610 | filling = fill_buffer(); | 1719 | filling = fill_buffer(); |
1611 | } | 1720 | } |
@@ -1618,9 +1727,14 @@ void buffering_init(void) | |||
1618 | { | 1727 | { |
1619 | mutex_init(&llist_mutex); | 1728 | mutex_init(&llist_mutex); |
1620 | 1729 | ||
1621 | conf_watermark = BUFFERING_DEFAULT_WATERMARK; | 1730 | /* Thread should absolutely not respond to USB because if it waits first, |
1622 | 1731 | then it cannot properly service the handles and leaks will happen - | |
1623 | queue_init(&buffering_queue, true); | 1732 | this is a worker thread and shouldn't need to care about any system |
1733 | notifications. | ||
1734 | *** | ||
1735 | Whoever is using buffering should be responsible enough to clear all | ||
1736 | the handles at the right time. */ | ||
1737 | queue_init(&buffering_queue, false); | ||
1624 | buffering_thread_id = create_thread( buffering_thread, buffering_stack, | 1738 | buffering_thread_id = create_thread( buffering_thread, buffering_stack, |
1625 | sizeof(buffering_stack), CREATE_THREAD_FROZEN, | 1739 | sizeof(buffering_stack), CREATE_THREAD_FROZEN, |
1626 | buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING) | 1740 | buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING) |
@@ -1636,6 +1750,9 @@ bool buffering_reset(char *buf, size_t buflen) | |||
1636 | /* Wraps of storage-aligned data must also be storage aligned, | 1750 | /* Wraps of storage-aligned data must also be storage aligned, |
1637 | thus buf and buflen must be a aligned to an integer multiple of | 1751 | thus buf and buflen must be a aligned to an integer multiple of |
1638 | the storage alignment */ | 1752 | the storage alignment */ |
1753 | |||
1754 | buflen -= GUARD_BUFSIZE; | ||
1755 | |||
1639 | STORAGE_ALIGN_BUFFER(buf, buflen); | 1756 | STORAGE_ALIGN_BUFFER(buf, buflen); |
1640 | 1757 | ||
1641 | if (!buf || !buflen) | 1758 | if (!buf || !buflen) |
@@ -1654,10 +1771,13 @@ bool buffering_reset(char *buf, size_t buflen) | |||
1654 | num_handles = 0; | 1771 | num_handles = 0; |
1655 | base_handle_id = -1; | 1772 | base_handle_id = -1; |
1656 | 1773 | ||
1657 | /* Set the high watermark as 75% full...or 25% empty :) */ | 1774 | /* Set the high watermark as 75% full...or 25% empty :) |
1658 | #if MEMORYSIZE > 8 | 1775 | This is the greatest fullness that will trigger low-buffer events |
1776 | no matter what the setting because high-bitrate files can have | ||
1777 | ludicrous margins that even exceed the buffer size - most common | ||
1778 | with a huge anti-skip buffer but even without that setting, | ||
1779 | staying constantly active in buffering is pointless */ | ||
1659 | high_watermark = 3*buflen / 4; | 1780 | high_watermark = 3*buflen / 4; |
1660 | #endif | ||
1661 | 1781 | ||
1662 | thread_thaw(buffering_thread_id); | 1782 | thread_thaw(buffering_thread_id); |
1663 | 1783 | ||
@@ -1673,5 +1793,5 @@ void buffering_get_debugdata(struct buffering_debug *dbgdata) | |||
1673 | dbgdata->wasted_space = dc.wasted; | 1793 | dbgdata->wasted_space = dc.wasted; |
1674 | dbgdata->buffered_data = dc.buffered; | 1794 | dbgdata->buffered_data = dc.buffered; |
1675 | dbgdata->useful_data = dc.useful; | 1795 | dbgdata->useful_data = dc.useful; |
1676 | dbgdata->watermark = conf_watermark; | 1796 | dbgdata->watermark = BUF_WATERMARK; |
1677 | } | 1797 | } |