From 342417c473ee5acef88a2dd4f71880651a808617 Mon Sep 17 00:00:00 2001 From: jethead71 Date: Sun, 16 May 2010 14:41:00 +0000 Subject: [PATCH] MPEGPlayer: Add a second layer of caching to help speed up byte-wise scanning and seeking a bit. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@26088 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/mpegplayer/disk_buf.c | 150 ++++++++++++++++++++++++++-------- apps/plugins/mpegplayer/disk_buf.h | 42 +++++----- apps/plugins/mpegplayer/mpeg_misc.c | 6 ++ apps/plugins/mpegplayer/mpeg_misc.h | 6 ++ apps/plugins/mpegplayer/mpeg_parser.c | 50 +++++++----- 5 files changed, 178 insertions(+), 76 deletions(-) diff --git a/apps/plugins/mpegplayer/disk_buf.c b/apps/plugins/mpegplayer/disk_buf.c index 0720197f3..39563b704 100644 --- a/apps/plugins/mpegplayer/disk_buf.c +++ b/apps/plugins/mpegplayer/disk_buf.c @@ -49,6 +49,26 @@ static inline void disk_buf_on_clear_data_notify(struct stream_hdr *sh) list_remove_item(&sh->nf); } + +inline bool disk_buf_is_data_ready(struct stream_hdr *sh, + ssize_t margin) +{ + /* Data window available? */ + off_t right = sh->win_right; + + /* Margins past end-of-file can still return true */ + if (right > disk_buf.filesize - margin) + right = disk_buf.filesize - margin; + + return sh->win_left >= disk_buf.win_left && + right + margin <= disk_buf.win_right; +} + +void dbuf_l2_init(struct dbuf_l2_cache *l2_p) +{ + l2_p->addr = OFF_T_MAX; /* Mark as invalid */ +} + static int disk_buf_on_data_notify(struct stream_hdr *sh) { DEBUGF("DISK_BUF_DATA_NOTIFY: 0x%02X ", STR_FROM_HEADER(sh)->id); @@ -519,17 +539,16 @@ static void disk_buf_thread(void) } /* Caches some data from the current file */ -static int disk_buf_probe(off_t start, size_t length, - void **p, size_t *outlen) +static ssize_t disk_buf_probe(off_t start, size_t length, void **p) { off_t end; uint32_t tag, tag_end; int page; /* Can't read past end of file */ - if (length > (size_t)(disk_buf.filesize - disk_buf.offset)) + if (length > (size_t)(disk_buf.filesize - start)) { - length = disk_buf.filesize - disk_buf.offset; + length = disk_buf.filesize - start; } /* Can't cache more than the whole buffer size */ @@ -559,11 +578,6 @@ static int disk_buf_probe(off_t start, size_t length, + (start & DISK_BUF_PAGE_MASK); } - if (outlen != NULL) - { - *outlen = length; - } - /* Obtain initial load point. If all data was cached, no message is sent * otherwise begin on the first page that is not cached. Since we have to * send the message anyway, the buffering thread will determine what else @@ -573,12 +587,17 @@ static int disk_buf_probe(off_t start, size_t length, if (disk_buf.cache[page] != tag) { static struct dbuf_range rng IBSS_ATTR; + intptr_t result; + DEBUGF("disk_buf: cache miss\n"); rng.tag_start = tag; rng.tag_end = tag_end; rng.pg_start = page; - return rb->queue_send(disk_buf.q, DISK_BUF_CACHE_RANGE, - (intptr_t)&rng); + + result = rb->queue_send(disk_buf.q, DISK_BUF_CACHE_RANGE, + (intptr_t)&rng); + + return result == DISK_BUF_NOTIFY_OK ? (ssize_t)length : -1; } if (++page >= disk_buf.pgcount) @@ -586,35 +605,96 @@ static int disk_buf_probe(off_t start, size_t length, } while (++tag <= tag_end); - return DISK_BUF_NOTIFY_OK; + return length; } /* Attempt to get a pointer to size bytes on the buffer. Returns real amount of * data available as well as the size of non-wrapped data after *p. */ -ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap, size_t *sizewrap) +ssize_t disk_buf_getbuffer(size_t size, void **pp, void **pwrap, + size_t *sizewrap) { disk_buf_lock(); - if (disk_buf_probe(disk_buf.offset, size, pp, &size) == DISK_BUF_NOTIFY_OK) + size = disk_buf_probe(disk_buf.offset, size, pp); + + if (size != (size_t)-1 && pwrap && sizewrap) { - if (pwrap && sizewrap) - { - uint8_t *p = (uint8_t *)*pp; + uint8_t *p = (uint8_t *)*pp; - if (p + size > disk_buf.end + DISK_GUARDBUF_SIZE) - { - /* Return pointer to wraparound and the size of same */ - size_t nowrap = (disk_buf.end + DISK_GUARDBUF_SIZE) - p; - *pwrap = disk_buf.start + DISK_GUARDBUF_SIZE; - *sizewrap = size - nowrap; - } - else - { - *pwrap = NULL; - *sizewrap = 0; - } + if (p + size > disk_buf.end + DISK_GUARDBUF_SIZE) + { + /* Return pointer to wraparound and the size of same */ + size_t nowrap = (disk_buf.end + DISK_GUARDBUF_SIZE) - p; + *pwrap = disk_buf.start + DISK_GUARDBUF_SIZE; + *sizewrap = size - nowrap; + } + else + { + *pwrap = NULL; + *sizewrap = 0; } } + + disk_buf_unlock(); + + return size; +} + +ssize_t disk_buf_getbuffer_l2(struct dbuf_l2_cache *l2, + size_t size, void **pp) +{ + off_t offs; + off_t l2_addr; + size_t l2_size; + void *l2_p; + + if (l2 == NULL) + { + /* Shouldn't have to check this normally */ + DEBUGF("disk_buf_getbuffer_l2: l2 = NULL!\n"); + } + + if (size > DISK_BUF_L2_CACHE_SIZE) + { + /* Asking for too much; just go through L1 */ + return disk_buf_getbuffer(size, pp, NULL, NULL); + } + + offs = disk_buf.offset; /* Other calls keep this within bounds */ + l2_addr = l2->addr; + + if (offs >= l2_addr && offs < l2_addr + DISK_BUF_L2_CACHE_SIZE) + { + /* Data is in the local buffer */ + offs &= DISK_BUF_L2_CACHE_MASK; + + *pp = l2->data + offs; + if (offs + size > l2->size) + size = l2->size - offs; /* Keep size within file limits */ + + return size; + } + + /* Have to probe main buffer */ + l2_addr = offs & ~DISK_BUF_L2_CACHE_MASK; + l2_size = DISK_BUF_L2_CACHE_SIZE*2; /* 2nd half is a guard buffer */ + + disk_buf_lock(); + + l2_size = disk_buf_probe(l2_addr, l2_size, &l2_p); + + if (l2_size != (size_t)-1) + { + rb->memcpy(l2->data, l2_p, l2_size); + + l2->addr = l2_addr; + l2->size = l2_size; + offs -= l2_addr; + + *pp = l2->data + offs; + if (offs + size > l2->size) + size = l2->size - offs; /* Keep size within file limits */ + } else { size = -1; @@ -625,6 +705,7 @@ ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap, size_t *sizewr return size; } + /* Read size bytes of data into a buffer - advances the buffer pointer * and returns the real size read. */ ssize_t disk_buf_read(void *buffer, size_t size) @@ -633,8 +714,9 @@ ssize_t disk_buf_read(void *buffer, size_t size) disk_buf_lock(); - if (disk_buf_probe(disk_buf.offset, size, PUN_PTR(void **, &p), - &size) == DISK_BUF_NOTIFY_OK) + size = disk_buf_probe(disk_buf.offset, size, (void **)&p); + + if (size != (size_t)-1) { if (p + size > disk_buf.end + DISK_GUARDBUF_SIZE) { @@ -652,10 +734,6 @@ ssize_t disk_buf_read(void *buffer, size_t size) disk_buf.offset += size; } - else - { - size = -1; - } disk_buf_unlock(); @@ -713,7 +791,7 @@ ssize_t disk_buf_prepare_streaming(off_t pos, size_t len) DEBUGF("prepare streaming:\n pos:%ld len:%zu\n", pos, len); pos = disk_buf_lseek(pos, SEEK_SET); - disk_buf_probe(pos, len, NULL, &len); + len = disk_buf_probe(pos, len, NULL); DEBUGF(" probe done: pos:%ld len:%zu\n", pos, len); diff --git a/apps/plugins/mpegplayer/disk_buf.h b/apps/plugins/mpegplayer/disk_buf.h index e16939a92..a5a10cfe6 100644 --- a/apps/plugins/mpegplayer/disk_buf.h +++ b/apps/plugins/mpegplayer/disk_buf.h @@ -23,6 +23,10 @@ #ifndef DISK_BUF_H #define DISK_BUF_H +#ifndef OFF_T_MAX +#define OFF_T_MAX (~(off_t)1 << (sizeof (off_t)*8 - 1)) +#endif + #define DISK_BUF_PAGE_SHIFT 15 /* 32KB cache lines */ #define DISK_BUF_PAGE_SIZE (1 << DISK_BUF_PAGE_SHIFT) #define DISK_BUF_PAGE_MASK (DISK_BUF_PAGE_SIZE-1) @@ -58,6 +62,19 @@ struct dbuf_range int pg_start; }; +#define DISK_BUF_L2_CACHE_SHIFT 6 +#define DISK_BUF_L2_CACHE_SIZE (1 << DISK_BUF_L2_CACHE_SHIFT) +#define DISK_BUF_L2_CACHE_MASK (DISK_BUF_L2_CACHE_SIZE-1) + +struct dbuf_l2_cache +{ + off_t addr; /* L2 file offset */ + size_t size; /* Real size */ + uint8_t data[DISK_BUF_L2_CACHE_SIZE*2]; /* Local data and guard */ +}; + +void dbuf_l2_init(struct dbuf_l2_cache *l2_p); + /* This object is an extension of the stream manager and handles some * playback events as well as buffering */ struct disk_buf @@ -88,20 +105,8 @@ struct disk_buf extern struct disk_buf disk_buf SHAREDBSS_ATTR; -static inline bool disk_buf_is_data_ready(struct stream_hdr *sh, - ssize_t margin) -{ - /* Data window available? */ - off_t right = sh->win_right; - - /* Margins past end-of-file can still return true */ - if (right > disk_buf.filesize - margin) - right = disk_buf.filesize - margin; - - return sh->win_left >= disk_buf.win_left && - right + margin <= disk_buf.win_right; -} - +struct stream_hdr; +bool disk_buf_is_data_ready(struct stream_hdr *sh, ssize_t margin); bool disk_buf_init(void); void disk_buf_exit(void); @@ -111,11 +116,10 @@ static inline int disk_buf_status(void) int disk_buf_open(const char *filename); void disk_buf_close(void); -ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap, - size_t *sizewrap); -#define disk_buf_getbuffer(size, pp, pwrap, sizewrap) \ - _disk_buf_getbuffer((size), PUN_PTR(void **, (pp)), \ - PUN_PTR(void **, (pwrap)), (sizewrap)) +ssize_t disk_buf_getbuffer(size_t size, void **pp, void **pwrap, + size_t *sizewrap); +ssize_t disk_buf_getbuffer_l2(struct dbuf_l2_cache *l2, + size_t size, void **pp); ssize_t disk_buf_read(void *buffer, size_t size); ssize_t disk_buf_lseek(off_t offset, int whence); diff --git a/apps/plugins/mpegplayer/mpeg_misc.c b/apps/plugins/mpegplayer/mpeg_misc.c index fd564a49c..8e6ccf650 100644 --- a/apps/plugins/mpegplayer/mpeg_misc.c +++ b/apps/plugins/mpegplayer/mpeg_misc.c @@ -25,6 +25,12 @@ /** Streams **/ +/* Initializes the cursor */ +void stream_scan_init(struct stream_scan *sk) +{ + dbuf_l2_init(&sk->l2); +} + /* Ensures direction is -1 or 1 and margin is properly initialized */ void stream_scan_normalize(struct stream_scan *sk) { diff --git a/apps/plugins/mpegplayer/mpeg_misc.h b/apps/plugins/mpegplayer/mpeg_misc.h index 7bac7eb25..5f60193d5 100644 --- a/apps/plugins/mpegplayer/mpeg_misc.h +++ b/apps/plugins/mpegplayer/mpeg_misc.h @@ -28,6 +28,8 @@ #define ALIGNED_ATTR(x) __attribute__((aligned(x))) #endif +#include "disk_buf.h" + /* Generic states for when things are too simple to care about naming them */ enum state_enum { @@ -158,11 +160,15 @@ struct stream_scan off_t dir; /* Direction - >= 0; forward, < 0 backward */ ssize_t margin; /* Used by function to track margin between position and data end */ intptr_t data; /* */ + struct dbuf_l2_cache l2; }; #define SSCAN_REVERSE (-1) #define SSCAN_FORWARD 1 +/* Initializes the cursor */ +void stream_scan_init(struct stream_scan *sk); + /* Ensures direction is -1 or 1 and margin is properly initialized */ void stream_scan_normalize(struct stream_scan *sk); diff --git a/apps/plugins/mpegplayer/mpeg_parser.c b/apps/plugins/mpegplayer/mpeg_parser.c index 714f38ac0..f21292abd 100644 --- a/apps/plugins/mpegplayer/mpeg_parser.c +++ b/apps/plugins/mpegplayer/mpeg_parser.c @@ -99,7 +99,7 @@ uint8_t * mpeg_parser_scan_start_code(struct stream_scan *sk, uint32_t code) { uint8_t *p; off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); - ssize_t len = disk_buf_getbuffer(4, &p, NULL, NULL); + ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, (void **)&p); if (pos < 0 || len < 4) break; @@ -131,7 +131,7 @@ unsigned mpeg_parser_scan_pes(struct stream_scan *sk) { uint8_t *p; off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); - ssize_t len = disk_buf_getbuffer(4, &p, NULL, NULL); + ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 4, (void **)&p); if (pos < 0 || len < 4) break; @@ -192,7 +192,7 @@ uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id) { uint8_t *p; off_t pos = disk_buf_lseek(sk->pos, SEEK_SET); - ssize_t len = disk_buf_getbuffer(35, &p, NULL, NULL); + ssize_t len = disk_buf_getbuffer_l2(&sk->l2, 30, (void **)&p); if (pos < 0 || len < 4) break; @@ -201,7 +201,7 @@ uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id) { uint8_t *h = p; - if (sk->margin < 6) + if (sk->margin < 7) { /* Insufficient data */ } @@ -215,29 +215,33 @@ uint32_t mpeg_parser_scan_pts(struct stream_scan *sk, unsigned id) } else /* mpeg1 */ { - ssize_t l = 7; + ssize_t l = 6; ssize_t margin = sk->margin; /* Skip stuffing_byte */ - while (h[l - 1] == 0xff && ++l <= 23) + while (margin > 7 && h[l] == 0xff && ++l <= 22) --margin; - if ((h[l - 1] & 0xc0) == 0x40) + if (margin >= 7) { - /* Skip STD_buffer_scale and STD_buffer_size */ - margin -= 2; - l += 2; - } - - if (margin >= 4) - { - /* header points to the mpeg1 pes header */ - h += l; + if ((h[l] & 0xc0) == 0x40) + { + /* Skip STD_buffer_scale and STD_buffer_size */ + margin -= 2; + l += 2; + } - if ((h[-1] & 0xe0) == 0x20) + if (margin >= 5) { - sk->data = (h + 4) - p; - return read_pts(h, -1); + /* Header points to the mpeg1 pes header */ + h += l; + + if ((h[0] & 0xe0) == 0x20) + { + /* PTS or PTS_DTS indicated */ + sk->data = (h + 5) - p; + return read_pts(h, 0); + } } } } @@ -357,6 +361,8 @@ static off_t mpeg_parser_seek_PTS(uint32_t time, unsigned id) enum state_enum state = STATE0; struct stream_scan sk; + stream_scan_init(&sk); + /* Initial estimate taken from average bitrate - later interpolations are * taken similarly based on the remaining file interval */ pos_new = muldiv_uint32(time - time_left, pos_right - pos_left, @@ -564,6 +570,8 @@ static bool prepare_image(uint32_t time) int tries; int result; + stream_scan_init(&sk); + if (!str_send_msg(&video_str, STREAM_NEEDS_SYNC, time)) { DEBUGF("Image was ready\n"); @@ -734,7 +742,7 @@ static int parse_demux(struct stream *str, enum stream_parse_mode type) str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET); if (str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit || - disk_buf_getbuffer(MIN_BUFAHEAD, &p, NULL, NULL) <= 0) + disk_buf_getbuffer(MIN_BUFAHEAD, (void **)&p, NULL, NULL) <= 0) { str_end_of_stream(str); return STREAM_DATA_END; @@ -1009,7 +1017,7 @@ static int parse_elementary(struct stream *str, enum stream_parse_mode type) case STREAM_PM_RANDOM_ACCESS: str->hdr.pos = disk_buf_lseek(str->hdr.pos, SEEK_SET); - len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, &p, NULL, NULL); + len = disk_buf_getbuffer(DISK_BUF_PAGE_SIZE, (void **)&p, NULL, NULL); if (len <= 0 || str->hdr.pos < 0 || str->hdr.pos >= str->hdr.limit) { -- 2.11.4.GIT