forked from len0rd/rockbox
Major recording rework: (1) Slight optimisation of the recording transfer. (2) Rework of the recording event loop: (a) When starting a recording, wait a bit longer before grabbing a header, increasing the chance that we get a valid one. (b) Bugfix: Always flush the whole buffer when it gets above the watermark. (c) Save in chunks for lower latency (1MB on 8MB-modded boxes, and 256KB on Ondio). (d) Hierarchical scheme of reasons to save data: stop_recording beats new_file, and new_file beats buffer_full. (e) Saving is done in one location. Decreased code size.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6560 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
d9b66127ac
commit
e03f40284e
1 changed files with 125 additions and 188 deletions
227
firmware/mpeg.c
227
firmware/mpeg.c
|
|
@ -340,12 +340,17 @@ static long lowest_watermark_level; /* Debug value to observe the buffer
|
||||||
usage */
|
usage */
|
||||||
#if CONFIG_HWCODEC == MAS3587F
|
#if CONFIG_HWCODEC == MAS3587F
|
||||||
static bool is_recording; /* We are recording */
|
static bool is_recording; /* We are recording */
|
||||||
static bool stop_pending;
|
|
||||||
unsigned long record_start_time; /* Value of current_tick when recording
|
unsigned long record_start_time; /* Value of current_tick when recording
|
||||||
was started */
|
was started */
|
||||||
unsigned long pause_start_time; /* Value of current_tick when pause was
|
unsigned long pause_start_time; /* Value of current_tick when pause was
|
||||||
started */
|
started */
|
||||||
static bool saving; /* We are saving the buffer to disk */
|
static enum {
|
||||||
|
NOT_SAVING = 0, /* reasons to save data, sorted by importance */
|
||||||
|
BUFFER_FULL,
|
||||||
|
NEW_FILE,
|
||||||
|
STOP_RECORDING
|
||||||
|
} saving_status;
|
||||||
|
|
||||||
static char recording_filename[MAX_PATH]; /* argument to thread */
|
static char recording_filename[MAX_PATH]; /* argument to thread */
|
||||||
static char delayed_filename[MAX_PATH]; /* internal copy of above */
|
static char delayed_filename[MAX_PATH]; /* internal copy of above */
|
||||||
static int rec_frequency_index; /* For create_xing_header() calls */
|
static int rec_frequency_index; /* For create_xing_header() calls */
|
||||||
|
|
@ -551,16 +556,18 @@ void rec_tick(void)
|
||||||
timing_info[timing_info_index++] = current_tick;
|
timing_info[timing_info_index++] = current_tick;
|
||||||
TCNT2 = 0;
|
TCNT2 = 0;
|
||||||
#endif /* #ifdef DEBUG */
|
#endif /* #ifdef DEBUG */
|
||||||
|
/* Note: Although this loop is run in interrupt context, further
|
||||||
|
* optimisation will do no good. The MAS would then deliver bad
|
||||||
|
* frames occasionally, as observed in extended experiments. */
|
||||||
i = 0;
|
i = 0;
|
||||||
while (PBDRH & 0x40) /* We try to read as long as EOD is high */
|
while (PBDRH & 0x40) /* We try to read as long as EOD is high */
|
||||||
{
|
{
|
||||||
xor_b(0x08, &PADRH); /* Set PR active, independent of polarity */
|
xor_b(0x08, &PADRH); /* Set PR active, independent of polarity */
|
||||||
|
|
||||||
delay = 0;
|
delay = 100;
|
||||||
while (PBDRH & 0x80) /* Wait until /RTW becomes active */
|
while (PBDRH & 0x80) /* Wait until /RTW becomes active */
|
||||||
{
|
{
|
||||||
delay++;
|
if (--delay <= 0) /* Bail out if we have to wait too long */
|
||||||
if (delay > 100) /* Bail out if we have to wait too long */
|
|
||||||
{ /* i.e. the MAS doesn't want to talk to us */
|
{ /* i.e. the MAS doesn't want to talk to us */
|
||||||
xor_b(0x08, &PADRH); /* Set PR inactive */
|
xor_b(0x08, &PADRH); /* Set PR inactive */
|
||||||
goto transfer_end; /* and get out of here */
|
goto transfer_end; /* and get out of here */
|
||||||
|
|
@ -613,9 +620,10 @@ void rec_tick(void)
|
||||||
if(num_bytes < 0)
|
if(num_bytes < 0)
|
||||||
num_bytes += audiobuflen;
|
num_bytes += audiobuflen;
|
||||||
|
|
||||||
if(audiobuflen - num_bytes < MPEG_RECORDING_LOW_WATER && !saving)
|
if (audiobuflen - num_bytes < MPEG_RECORDING_LOW_WATER
|
||||||
|
&& saving_status == NOT_SAVING)
|
||||||
{
|
{
|
||||||
saving = true;
|
saving_status = BUFFER_FULL;
|
||||||
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
||||||
wake_up_thread();
|
wake_up_thread();
|
||||||
}
|
}
|
||||||
|
|
@ -998,13 +1006,11 @@ static void mpeg_thread(void)
|
||||||
int start_offset;
|
int start_offset;
|
||||||
#if CONFIG_HWCODEC == MAS3587F
|
#if CONFIG_HWCODEC == MAS3587F
|
||||||
int amount_to_save;
|
int amount_to_save;
|
||||||
int writelen;
|
|
||||||
int framelen;
|
int framelen;
|
||||||
unsigned long saved_header = 0;
|
unsigned long saved_header = 0;
|
||||||
int startpos;
|
int save_endpos = 0;
|
||||||
int rc;
|
int rc;
|
||||||
long offset;
|
long offset;
|
||||||
int countdown;
|
|
||||||
#endif /* #if CONFIG_HWCODEC == MAS3587F */
|
#endif /* #if CONFIG_HWCODEC == MAS3587F */
|
||||||
|
|
||||||
is_playing = false;
|
is_playing = false;
|
||||||
|
|
@ -1640,7 +1646,7 @@ static void mpeg_thread(void)
|
||||||
/* Wait until at least one frame is encoded and get the
|
/* Wait until at least one frame is encoded and get the
|
||||||
frame header, for later use by the Xing header
|
frame header, for later use by the Xing header
|
||||||
generation */
|
generation */
|
||||||
sleep(HZ/10);
|
sleep(HZ/5);
|
||||||
saved_header = mpeg_get_last_header();
|
saved_header = mpeg_get_last_header();
|
||||||
|
|
||||||
/* delayed until buffer is saved, don't open yet */
|
/* delayed until buffer is saved, don't open yet */
|
||||||
|
|
@ -1655,7 +1661,8 @@ static void mpeg_thread(void)
|
||||||
stop_recording();
|
stop_recording();
|
||||||
|
|
||||||
/* Save the remaining data in the buffer */
|
/* Save the remaining data in the buffer */
|
||||||
stop_pending = true;
|
save_endpos = audiobuf_write;
|
||||||
|
saving_status = STOP_RECORDING;
|
||||||
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -1720,56 +1727,58 @@ static void mpeg_thread(void)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPEG_NEW_FILE:
|
case MPEG_NEW_FILE:
|
||||||
|
/* Bail out when a more important save is happening */
|
||||||
|
if (saving_status > NEW_FILE)
|
||||||
|
break;
|
||||||
|
|
||||||
/* Make sure we have at least one complete frame
|
/* Make sure we have at least one complete frame
|
||||||
in the buffer. If we haven't recorded a single
|
in the buffer. If we haven't recorded a single
|
||||||
frame within 200ms, the MAS is probably not recording
|
frame within 200ms, the MAS is probably not recording
|
||||||
anything, and we bail out. */
|
anything, and we bail out. */
|
||||||
countdown = 20;
|
|
||||||
amount_to_save = get_unsaved_space();
|
amount_to_save = get_unsaved_space();
|
||||||
while(countdown-- && amount_to_save < 1800)
|
if (amount_to_save < 1800)
|
||||||
{
|
{
|
||||||
sleep(HZ/10);
|
sleep(HZ/5);
|
||||||
amount_to_save = get_unsaved_space();
|
amount_to_save = get_unsaved_space();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount_to_save >= 1800)
|
if (amount_to_save >= 1800)
|
||||||
{
|
{
|
||||||
/* Now find a frame boundary to split at */
|
/* Now find a frame boundary to split at */
|
||||||
startpos = audiobuf_write - 1800;
|
save_endpos = audiobuf_write - 1800;
|
||||||
if(startpos < 0)
|
if (save_endpos < 0)
|
||||||
startpos += audiobuflen;
|
save_endpos += audiobuflen;
|
||||||
|
|
||||||
rc = mem_find_next_frame(startpos, &offset, 1800,
|
rc = mem_find_next_frame(save_endpos, &offset, 1800,
|
||||||
saved_header);
|
saved_header);
|
||||||
if (rc) /* Header found? */
|
if (rc) /* Header found? */
|
||||||
{
|
{
|
||||||
/* offset will now contain the number of bytes to
|
/* offset will now contain the number of bytes to
|
||||||
add to startpos to find the frame boundary */
|
add to startpos to find the frame boundary */
|
||||||
startpos += offset;
|
save_endpos += offset;
|
||||||
if(startpos >= audiobuflen)
|
if (save_endpos >= audiobuflen)
|
||||||
startpos -= audiobuflen;
|
save_endpos -= audiobuflen;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* No header found. Let's save the whole buffer. */
|
/* No header found. Let's save the whole buffer. */
|
||||||
startpos = audiobuf_write;
|
save_endpos = audiobuf_write;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Too few bytes recorded, timeout */
|
/* Too few bytes recorded, timeout */
|
||||||
startpos = audiobuf_write;
|
save_endpos = audiobuf_write;
|
||||||
}
|
}
|
||||||
|
|
||||||
amount_to_save = startpos - audiobuf_read;
|
saving_status = NEW_FILE;
|
||||||
if(amount_to_save < 0)
|
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
||||||
amount_to_save += audiobuflen;
|
|
||||||
|
|
||||||
/* First save up to the end of the buffer */
|
case MPEG_SAVE_DATA:
|
||||||
writelen = MIN(amount_to_save,
|
if (saving_status == BUFFER_FULL)
|
||||||
audiobuflen - audiobuf_read);
|
save_endpos = audiobuf_write;
|
||||||
|
|
||||||
if (mpeg_file < 0) /* delayed file opening */
|
if (mpeg_file < 0) /* delayed file open */
|
||||||
{
|
{
|
||||||
mpeg_file = open(delayed_filename, O_WRONLY|O_CREAT);
|
mpeg_file = open(delayed_filename, O_WRONLY|O_CREAT);
|
||||||
|
|
||||||
|
|
@ -1777,103 +1786,20 @@ static void mpeg_thread(void)
|
||||||
panicf("recfile: %d", mpeg_file);
|
panicf("recfile: %d", mpeg_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(writelen)
|
amount_to_save = save_endpos - audiobuf_read;
|
||||||
{
|
|
||||||
rc = write(mpeg_file, audiobuf + audiobuf_read, writelen);
|
|
||||||
if(rc < 0)
|
|
||||||
{
|
|
||||||
if(errno == ENOSPC)
|
|
||||||
{
|
|
||||||
mpeg_errno = AUDIOERR_DISK_FULL;
|
|
||||||
demand_irq_enable(false);
|
|
||||||
stop_recording();
|
|
||||||
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
panicf("spt wrt: %d", rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Then save the rest */
|
|
||||||
writelen = amount_to_save - writelen;
|
|
||||||
if(writelen)
|
|
||||||
{
|
|
||||||
rc = write(mpeg_file, audiobuf, writelen);
|
|
||||||
if(rc < 0)
|
|
||||||
{
|
|
||||||
if(errno == ENOSPC)
|
|
||||||
{
|
|
||||||
mpeg_errno = AUDIOERR_DISK_FULL;
|
|
||||||
demand_irq_enable(false);
|
|
||||||
stop_recording();
|
|
||||||
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
panicf("spt wrt: %d", rc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advance the buffer pointers */
|
|
||||||
audiobuf_read += amount_to_save;
|
|
||||||
if(audiobuf_read >= audiobuflen)
|
|
||||||
audiobuf_read -= audiobuflen;
|
|
||||||
|
|
||||||
/* Close the current file */
|
|
||||||
rc = close(mpeg_file);
|
|
||||||
if(rc < 0)
|
|
||||||
panicf("spt cls: %d", rc);
|
|
||||||
|
|
||||||
/* Open the new file */
|
|
||||||
mpeg_file = open(recording_filename, O_WRONLY|O_CREAT);
|
|
||||||
if(mpeg_file < 0)
|
|
||||||
panicf("sptfile: %d", mpeg_file);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MPEG_SAVE_DATA:
|
|
||||||
amount_to_save = get_unsaved_space();
|
|
||||||
|
|
||||||
/* If the result is negative, the write index has
|
|
||||||
wrapped */
|
|
||||||
if (amount_to_save < 0)
|
if (amount_to_save < 0)
|
||||||
{
|
|
||||||
amount_to_save += audiobuflen;
|
amount_to_save += audiobuflen;
|
||||||
}
|
|
||||||
|
|
||||||
DEBUGF("r: %x w: %x\n", audiobuf_read, audiobuf_write);
|
amount_to_save = MIN(amount_to_save,
|
||||||
DEBUGF("ats: %x\n", amount_to_save);
|
|
||||||
|
|
||||||
/* Save data only if the buffer is getting full,
|
|
||||||
or if we should stop recording */
|
|
||||||
if(amount_to_save)
|
|
||||||
{
|
|
||||||
if(audiobuflen -
|
|
||||||
amount_to_save < MPEG_RECORDING_LOW_WATER ||
|
|
||||||
stop_pending)
|
|
||||||
{
|
|
||||||
/* Only save up to the end of the buffer */
|
|
||||||
writelen = MIN(amount_to_save,
|
|
||||||
audiobuflen - audiobuf_read);
|
audiobuflen - audiobuf_read);
|
||||||
|
#if MEM == 8
|
||||||
DEBUGF("wrl: %x\n", writelen);
|
amount_to_save = MIN(0x100000, amount_to_save);
|
||||||
|
#endif
|
||||||
if (mpeg_file < 0) /* delayed file opening */
|
#ifdef HAVE_MMC /* MMC is slow, so don't save too large chunks at once */
|
||||||
{
|
amount_to_save = MIN(0x40000, amount_to_save);
|
||||||
mpeg_file = open(delayed_filename,
|
#endif
|
||||||
O_WRONLY|O_CREAT);
|
|
||||||
|
|
||||||
if(mpeg_file < 0)
|
|
||||||
panicf("recfile: %d", mpeg_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = write(mpeg_file, audiobuf + audiobuf_read,
|
rc = write(mpeg_file, audiobuf + audiobuf_read,
|
||||||
writelen);
|
amount_to_save);
|
||||||
|
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
{
|
{
|
||||||
if (errno == ENOSPC)
|
if (errno == ENOSPC)
|
||||||
|
|
@ -1881,39 +1807,52 @@ static void mpeg_thread(void)
|
||||||
mpeg_errno = AUDIOERR_DISK_FULL;
|
mpeg_errno = AUDIOERR_DISK_FULL;
|
||||||
stop_recording();
|
stop_recording();
|
||||||
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
|
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
|
||||||
|
/* will close the file */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
panicf("rec wrt: %d", rc);
|
panicf("rec wrt: %d", rc);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
audiobuf_read += amount_to_save;
|
audiobuf_read += amount_to_save;
|
||||||
if (audiobuf_read >= audiobuflen)
|
if (audiobuf_read >= audiobuflen)
|
||||||
audiobuf_read = 0;
|
audiobuf_read = 0;
|
||||||
|
|
||||||
|
if (audiobuf_read == save_endpos) /* all saved */
|
||||||
|
{
|
||||||
|
switch (saving_status)
|
||||||
|
{
|
||||||
|
case BUFFER_FULL:
|
||||||
rc = fsync(mpeg_file);
|
rc = fsync(mpeg_file);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
panicf("rec fls: %d", rc);
|
panicf("rec fls: %d", rc);
|
||||||
|
ata_sleep();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NEW_FILE:
|
||||||
|
/* Close the current file */
|
||||||
|
rc = close(mpeg_file);
|
||||||
|
if (rc < 0)
|
||||||
|
panicf("spt cls: %d", rc);
|
||||||
|
ata_sleep();
|
||||||
|
mpeg_file = -1;
|
||||||
|
/* copy new filename */
|
||||||
|
strcpy(delayed_filename, recording_filename);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case STOP_RECORDING:
|
||||||
|
queue_post(&mpeg_queue, MPEG_STOP_DONE, NULL);
|
||||||
|
/* will close the file */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
saving_status = NOT_SAVING;
|
||||||
|
}
|
||||||
|
else /* tell ourselves to save the next chunk */
|
||||||
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
saving = false;
|
|
||||||
ata_sleep();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* We have saved all data,
|
|
||||||
time to stop for real */
|
|
||||||
if(stop_pending)
|
|
||||||
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
|
|
||||||
saving = false;
|
|
||||||
ata_sleep();
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MPEG_INIT_PLAYBACK:
|
case MPEG_INIT_PLAYBACK:
|
||||||
|
|
@ -2200,8 +2139,7 @@ static void start_prerecording(void)
|
||||||
} while(val & 1);
|
} while(val & 1);
|
||||||
|
|
||||||
is_recording = true;
|
is_recording = true;
|
||||||
stop_pending = false;
|
saving_status = NOT_SAVING;
|
||||||
saving = false;
|
|
||||||
|
|
||||||
demand_irq_enable(true);
|
demand_irq_enable(true);
|
||||||
}
|
}
|
||||||
|
|
@ -2235,8 +2173,7 @@ static void start_recording(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
is_recording = true;
|
is_recording = true;
|
||||||
stop_pending = false;
|
saving_status = NOT_SAVING;
|
||||||
saving = false;
|
|
||||||
paused = false;
|
paused = false;
|
||||||
|
|
||||||
/* Store the current time */
|
/* Store the current time */
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue