1
0
Fork 0
forked from len0rd/rockbox

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
This commit is contained in:
Michael Sevakis 2010-05-16 14:41:00 +00:00
parent caa4f54e42
commit b197c3b0d6
5 changed files with 180 additions and 78 deletions

View file

@ -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,
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,18 +605,19 @@ 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)
{
if (pwrap && sizewrap)
size = disk_buf_probe(disk_buf.offset, size, pp);
if (size != (size_t)-1 && pwrap && sizewrap)
{
uint8_t *p = (uint8_t *)*pp;
@ -614,6 +634,66 @@ ssize_t _disk_buf_getbuffer(size_t size, void **pp, void **pwrap, size_t *sizewr
*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
{
@ -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);

View file

@ -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,
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_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);

View file

@ -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)
{

View file

@ -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);

View file

@ -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)
{
if ((h[l] & 0xc0) == 0x40)
{
/* Skip STD_buffer_scale and STD_buffer_size */
margin -= 2;
l += 2;
}
if (margin >= 4)
if (margin >= 5)
{
/* header points to the mpeg1 pes header */
/* Header points to the mpeg1 pes header */
h += l;
if ((h[-1] & 0xe0) == 0x20)
if ((h[0] & 0xe0) == 0x20)
{
sk->data = (h + 4) - p;
return read_pts(h, -1);
/* 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)
{