A rather big change to how tracks are loaded: there are now two parts to the process and metadata loading is done by the buffering thread (except for the first unbuffered track). The audio thread now calls audio_load_track, and once the metadata is loaded, the buffering thread sends an event which will make the audio thread call audio_finish_load_track. This one then takes care of the rest of the loading.

This method makes skipping noticeably faster for unbuffered tracks, and especially backwards.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17109 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Nicolas Pennequin 2008-04-14 16:17:47 +00:00
parent a5ad74ffca
commit 4e2de44b44
2 changed files with 161 additions and 87 deletions

View file

@ -50,6 +50,7 @@
#include "buffer.h" #include "buffer.h"
#include "bmp.h" #include "bmp.h"
#include "events.h" #include "events.h"
#include "metadata.h"
#ifdef SIMULATOR #ifdef SIMULATOR
#define ata_disk_is_active() 1 #define ata_disk_is_active() 1
@ -586,6 +587,18 @@ static bool buffer_handle(int handle_id)
trigger_cpu_boost(); trigger_cpu_boost();
if (h->type == TYPE_ID3)
{
get_metadata((struct mp3entry *)(buffer + h->data), h->fd, h->path);
close(h->fd);
h->fd = -1;
h->filerem = 0;
h->available = sizeof(struct mp3entry);
h->widx += sizeof(struct mp3entry);
send_event(EVENT_HANDLE_FINISHED, &h->id);
return true;
}
while (h->filerem > 0) while (h->filerem > 0)
{ {
/* max amount to copy */ /* max amount to copy */
@ -869,7 +882,31 @@ management functions for all the actual handle management work.
*/ */
int bufopen(const char *file, size_t offset, enum data_type type) int bufopen(const char *file, size_t offset, enum data_type type)
{ {
size_t adjusted_offset = offset; if (type == TYPE_ID3)
{
/* ID3 case: allocate space, init the handle and return. */
struct memory_handle *h = add_handle(sizeof(struct mp3entry), false, true);
if (!h)
return ERR_BUFFER_FULL;
h->fd = -1;
h->filesize = sizeof(struct mp3entry);
h->filerem = sizeof(struct mp3entry);
h->offset = 0;
h->data = buf_widx;
h->ridx = buf_widx;
h->widx = buf_widx;
h->available = 0;
h->type = type;
strncpy(h->path, file, MAX_PATH);
buf_widx += sizeof(struct mp3entry); /* safe because the handle
can't wrap */
return h->id;
}
/* Other cases: there is a little more work. */
int fd = open(file, O_RDONLY); int fd = open(file, O_RDONLY);
if (fd < 0) if (fd < 0)
@ -878,6 +915,7 @@ int bufopen(const char *file, size_t offset, enum data_type type)
size_t size = filesize(fd); size_t size = filesize(fd);
bool can_wrap = type==TYPE_PACKET_AUDIO || type==TYPE_CODEC; bool can_wrap = type==TYPE_PACKET_AUDIO || type==TYPE_CODEC;
size_t adjusted_offset = offset;
if (adjusted_offset > size) if (adjusted_offset > size)
adjusted_offset = 0; adjusted_offset = 0;

View file

@ -131,6 +131,7 @@ enum {
Q_AUDIO_DIR_SKIP, Q_AUDIO_DIR_SKIP,
Q_AUDIO_POSTINIT, Q_AUDIO_POSTINIT,
Q_AUDIO_FILL_BUFFER, Q_AUDIO_FILL_BUFFER,
Q_AUDIO_FINISH_LOAD,
Q_CODEC_REQUEST_COMPLETE, Q_CODEC_REQUEST_COMPLETE,
Q_CODEC_REQUEST_FAILED, Q_CODEC_REQUEST_FAILED,
@ -249,6 +250,9 @@ static bool new_playlist = false; /* Are we starting a new playlist? (A) */
static int wps_offset = 0; /* Pending track change offset, to keep WPS responsive (A) */ static int wps_offset = 0; /* Pending track change offset, to keep WPS responsive (A) */
static bool skipped_during_pause = false; /* Do we need to clear the PCM buffer when playback resumes (A) */ static bool skipped_during_pause = false; /* Do we need to clear the PCM buffer when playback resumes (A) */
static bool start_play_g = false; /* Used by audio_load_track to notify
audio_finish_load_track about start_play */
/* Set to true if the codec thread should send an audio stop request /* Set to true if the codec thread should send an audio stop request
* (typically because the end of the playlist has been reached). * (typically because the end of the playlist has been reached).
*/ */
@ -606,6 +610,9 @@ struct mp3entry* audio_next_track(void)
next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK; next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK;
if (tracks[next_idx].id3_hid >= 0)
return bufgetid3(tracks[next_idx].id3_hid);
if (next_idx == track_widx) if (next_idx == track_widx)
{ {
/* The next track hasn't been buffered yet, so we return the static /* The next track hasn't been buffered yet, so we return the static
@ -613,10 +620,7 @@ struct mp3entry* audio_next_track(void)
return &lasttrack_id3; return &lasttrack_id3;
} }
if (tracks[next_idx].id3_hid < 0) return NULL;
return NULL;
else
return bufgetid3(tracks[next_idx].id3_hid);
} }
bool audio_has_changed_track(void) bool audio_has_changed_track(void)
@ -1413,7 +1417,20 @@ static void buffering_handle_rebuffer_callback(void *data)
static void buffering_handle_finished_callback(int *data) static void buffering_handle_finished_callback(int *data)
{ {
logf("handle %d finished buffering", *data); logf("handle %d finished buffering", *data);
strip_tags(*data);
if (*data == tracks[track_widx].id3_hid)
{
/* The metadata handle for the last loaded track has been buffered.
We can ask the audio thread to load the rest of the track's data. */
LOGFQUEUE("audio >| audio Q_AUDIO_FINISH_LOAD");
queue_post(&audio_queue, Q_AUDIO_FINISH_LOAD, 0);
}
else
{
/* This is most likely an audio handle, so we strip the useless
trailing tags that are left. */
strip_tags(*data);
}
} }
@ -1446,13 +1463,9 @@ long audio_filebufused(void)
static void audio_update_trackinfo(void) static void audio_update_trackinfo(void)
{ {
/* Load the curent track's metadata into curtrack_id3 */ /* Load the curent track's metadata into curtrack_id3 */
CUR_TI->taginfo_ready = (CUR_TI->id3_hid >= 0);
if (CUR_TI->id3_hid >= 0) if (CUR_TI->id3_hid >= 0)
copy_mp3entry(&curtrack_id3, bufgetid3(CUR_TI->id3_hid)); copy_mp3entry(&curtrack_id3, bufgetid3(CUR_TI->id3_hid));
int next_idx = (track_ridx + 1) & MAX_TRACK_MASK;
tracks[next_idx].taginfo_ready = (tracks[next_idx].id3_hid >= 0);
/* Reset current position */ /* Reset current position */
curtrack_id3.elapsed = 0; curtrack_id3.elapsed = 0;
curtrack_id3.offset = 0; curtrack_id3.offset = 0;
@ -1560,15 +1573,15 @@ static bool audio_loadcodec(bool start_play)
return true; return true;
} }
/* Load one track by making the appropriate bufopen calls. Return true if /* Load metadata for the next track (with bufopen). The rest of the track
everything required was loaded correctly, false if not. */ loading will be handled by audio_finish_load_track once the metadata has been
static bool audio_load_track(int offset, bool start_play) actually loaded by the buffering thread. */
static bool audio_load_track(size_t offset, bool start_play)
{ {
const char *trackname; const char *trackname;
char msgbuf[80];
int fd = -1; int fd = -1;
int file_offset = 0;
struct mp3entry id3; start_play_g = start_play; /* will be read by audio_finish_load_track */
/* Stop buffer filling if there is no free track entries. /* Stop buffer filling if there is no free track entries.
Don't fill up the last track entry (we wan't to store next track Don't fill up the last track entry (we wan't to store next track
@ -1582,7 +1595,6 @@ static bool audio_load_track(int offset, bool start_play)
last_peek_offset++; last_peek_offset++;
tracks[track_widx].taginfo_ready = false; tracks[track_widx].taginfo_ready = false;
peek_again:
logf("Buffering track:%d/%d", track_widx, track_ridx); logf("Buffering track:%d/%d", track_widx, track_ridx);
/* Get track name from current playlist read position. */ /* Get track name from current playlist read position. */
while ((trackname = playlist_peek(last_peek_offset)) != NULL) while ((trackname = playlist_peek(last_peek_offset)) != NULL)
@ -1610,7 +1622,7 @@ static bool audio_load_track(int offset, bool start_play)
tracks[track_widx].filesize = filesize(fd); tracks[track_widx].filesize = filesize(fd);
if ((unsigned)offset > tracks[track_widx].filesize) if (offset > tracks[track_widx].filesize)
offset = 0; offset = 0;
/* Set default values */ /* Set default values */
@ -1625,43 +1637,48 @@ static bool audio_load_track(int offset, bool start_play)
/* Get track metadata if we don't already have it. */ /* Get track metadata if we don't already have it. */
if (tracks[track_widx].id3_hid < 0) if (tracks[track_widx].id3_hid < 0)
{ {
if (get_metadata(&id3, fd, trackname)) tracks[track_widx].id3_hid = bufopen(trackname, 0, TYPE_ID3);
if (tracks[track_widx].id3_hid < 0)
{ {
send_event(PLAYBACK_EVENT_TRACK_BUFFER, &id3); /* Buffer is full. */
get_metadata(&lasttrack_id3, fd, trackname);
tracks[track_widx].id3_hid = last_peek_offset--;
bufalloc(&id3, sizeof(struct mp3entry), TYPE_ID3);
if (tracks[track_widx].id3_hid < 0)
{
last_peek_offset--;
close(fd);
copy_mp3entry(&lasttrack_id3, &id3);
goto buffer_full;
}
if (track_widx == track_ridx)
copy_mp3entry(&curtrack_id3, &id3);
if (start_play)
{
track_changed = true;
playlist_update_resume_info(audio_current_track());
}
}
else
{
logf("mde:%s!",trackname);
/* Skip invalid entry from playlist. */
playlist_skip_entry(NULL, last_peek_offset);
close(fd); close(fd);
goto peek_again; logf("buffer is full for now");
filling = STATE_FULL;
return false;
} }
close(fd);
if (track_widx == track_ridx)
{
buf_request_buffer_handle(tracks[track_widx].id3_hid);
copy_mp3entry(&curtrack_id3, bufgetid3(tracks[track_widx].id3_hid));
curtrack_id3.offset = offset;
}
if (start_play)
{
track_changed = true;
playlist_update_resume_info(audio_current_track());
}
} }
close(fd); return true;
}
/* Second part of the track loading: We now have the metadata available, so we
can load the codec, the album art and finally the audio data.
This is called on the audio thread after the buffering thread calls the
buffering_handle_finished_callback callback. */
static void audio_finish_load_track(void)
{
char msgbuf[80];
size_t file_offset = 0;
size_t offset = 0;
bool start_play = start_play_g;
#if 0 #if 0
if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1) if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1)
@ -1680,6 +1697,11 @@ static bool audio_load_track(int offset, bool start_play)
} }
#endif #endif
if (tracks[track_widx].id3_hid < 0) {
logf("no metatdata");
return;
}
struct mp3entry *track_id3; struct mp3entry *track_id3;
if (track_widx == track_ridx) if (track_widx == track_ridx)
@ -1687,6 +1709,24 @@ static bool audio_load_track(int offset, bool start_play)
else else
track_id3 = bufgetid3(tracks[track_widx].id3_hid); track_id3 = bufgetid3(tracks[track_widx].id3_hid);
if (track_id3->length == 0 || track_id3->filesize == 0)
{
logf("audio_finish_load_track: invalid metadata");
/* Invalid metadata */
bufclose(tracks[track_widx].id3_hid);
tracks[track_widx].id3_hid = -1;
/* Skip invalid entry from playlist. */
playlist_skip_entry(NULL, last_peek_offset--);
/* load next track */
LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER %d", (int)start_play);
queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, start_play);
return;
}
#ifdef HAVE_ALBUMART #ifdef HAVE_ALBUMART
if (tracks[track_widx].aa_hid < 0 && gui_sync_wps_uses_albumart()) if (tracks[track_widx].aa_hid < 0 && gui_sync_wps_uses_albumart())
{ {
@ -1702,20 +1742,21 @@ static bool audio_load_track(int offset, bool start_play)
if (tracks[track_widx].codec_hid == ERR_BUFFER_FULL) if (tracks[track_widx].codec_hid == ERR_BUFFER_FULL)
{ {
/* No space for codec on buffer, not an error */ /* No space for codec on buffer, not an error */
goto buffer_full; return;
} }
/* This is an error condition, either no codec was found, or reading /* This is an error condition, either no codec was found, or reading
* the codec file failed part way through, either way, skip the track */ * the codec file failed part way through, either way, skip the track */
snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname); snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", track_id3->path);
/* We should not use gui_syncplash from audio thread! */ /* We should not use gui_syncplash from audio thread! */
gui_syncsplash(HZ*2, msgbuf); gui_syncsplash(HZ*2, msgbuf);
/* Skip invalid entry from playlist. */ /* Skip invalid entry from playlist. */
playlist_skip_entry(NULL, last_peek_offset); playlist_skip_entry(NULL, last_peek_offset);
goto peek_again; return;
} }
track_id3->elapsed = 0; track_id3->elapsed = 0;
offset = track_id3->offset;
enum data_type type = TYPE_PACKET_AUDIO; enum data_type type = TYPE_PACKET_AUDIO;
@ -1758,7 +1799,7 @@ static bool audio_load_track(int offset, bool start_play)
break; break;
} }
logf("alt:%s", trackname); logf("alt:%s", track_id3->path);
if (file_offset > AUDIO_REBUFFER_GUESS_SIZE) if (file_offset > AUDIO_REBUFFER_GUESS_SIZE)
file_offset -= AUDIO_REBUFFER_GUESS_SIZE; file_offset -= AUDIO_REBUFFER_GUESS_SIZE;
@ -1767,10 +1808,10 @@ static bool audio_load_track(int offset, bool start_play)
else else
file_offset = 0; file_offset = 0;
tracks[track_widx].audio_hid = bufopen(trackname, file_offset, type); tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type);
if (tracks[track_widx].audio_hid < 0) if (tracks[track_widx].audio_hid < 0)
goto buffer_full; return;
/* All required data is now available for the codec. */ /* All required data is now available for the codec. */
tracks[track_widx].taginfo_ready = true; tracks[track_widx].taginfo_ready = true;
@ -1783,18 +1824,18 @@ static bool audio_load_track(int offset, bool start_play)
track_widx = (track_widx + 1) & MAX_TRACK_MASK; track_widx = (track_widx + 1) & MAX_TRACK_MASK;
return true; send_event(PLAYBACK_EVENT_TRACK_BUFFER, track_id3);
buffer_full: /* load next track */
logf("buffer is full for now"); LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER");
filling = STATE_FULL; queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
return false;
return;
} }
static void audio_fill_file_buffer(bool start_play, size_t offset) static void audio_fill_file_buffer(bool start_play, size_t offset)
{ {
bool had_next_track = audio_next_track() != NULL; bool had_next_track = audio_next_track() != NULL;
bool continue_buffering;
filling = STATE_FILLING; filling = STATE_FILLING;
trigger_cpu_boost(); trigger_cpu_boost();
@ -1817,18 +1858,10 @@ static void audio_fill_file_buffer(bool start_play, size_t offset)
/* Save the current resume position once. */ /* Save the current resume position once. */
playlist_update_resume_info(audio_current_track()); playlist_update_resume_info(audio_current_track());
continue_buffering = audio_load_track(offset, start_play); audio_load_track(offset, start_play);
do {
sleep(1);
if (!queue_empty(&audio_queue))
/* There's a message in the queue. break the loop to treat it */
break;
continue_buffering = audio_load_track(0, false);
} while (continue_buffering);
if (!had_next_track && audio_next_track()) if (!had_next_track && audio_next_track())
track_changed = true; track_changed = true;
} }
static void audio_rebuffer(void) static void audio_rebuffer(void)
@ -2199,22 +2232,22 @@ static void audio_finalise_track_change(void)
{ {
wps_offset = 0; wps_offset = 0;
automatic_skip = false; automatic_skip = false;
}
/* Invalidate prevtrack_id3 */ /* Invalidate prevtrack_id3 */
prevtrack_id3.path[0] = 0; prevtrack_id3.path[0] = 0;
if (prev_ti && prev_ti->audio_hid < 0) if (prev_ti && prev_ti->audio_hid < 0)
{ {
/* No audio left so we clear all the track info. */ /* No audio left so we clear all the track info. */
clear_track_info(prev_ti); clear_track_info(prev_ti);
} }
if (prev_ti && prev_ti->id3_hid >= 0) if (prev_ti && prev_ti->id3_hid >= 0)
{ {
/* Reset the elapsed time to force the progressbar to be empty if /* Reset the elapsed time to force the progressbar to be empty if
the user skips back to this track */ the user skips back to this track */
bufgetid3(prev_ti->id3_hid)->elapsed = 0; bufgetid3(prev_ti->id3_hid)->elapsed = 0;
}
} }
send_event(PLAYBACK_EVENT_TRACK_CHANGE, &curtrack_id3); send_event(PLAYBACK_EVENT_TRACK_CHANGE, &curtrack_id3);
@ -2297,8 +2330,13 @@ static void audio_thread(void)
switch (ev.id) { switch (ev.id) {
case Q_AUDIO_FILL_BUFFER: case Q_AUDIO_FILL_BUFFER:
LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER"); LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER %d", (int)ev.data);
audio_fill_file_buffer(false, 0); audio_fill_file_buffer((bool)ev.data, 0);
break;
case Q_AUDIO_FINISH_LOAD:
LOGFQUEUE("audio < Q_AUDIO_FINISH_LOAD");
audio_finish_load_track();
break; break;
case Q_AUDIO_PLAY: case Q_AUDIO_PLAY:
@ -2400,8 +2438,6 @@ static void audio_thread(void)
case SYS_TIMEOUT: case SYS_TIMEOUT:
LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT"); LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT");
if (filling == STATE_FILLING)
audio_fill_file_buffer(false, 0);
break; break;
default: default: