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 */
|
||||
#if CONFIG_HWCODEC == MAS3587F
|
||||
static bool is_recording; /* We are recording */
|
||||
static bool stop_pending;
|
||||
unsigned long record_start_time; /* Value of current_tick when recording
|
||||
was started */
|
||||
unsigned long pause_start_time; /* Value of current_tick when pause was
|
||||
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 delayed_filename[MAX_PATH]; /* internal copy of above */
|
||||
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;
|
||||
TCNT2 = 0;
|
||||
#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;
|
||||
while (PBDRH & 0x40) /* We try to read as long as EOD is high */
|
||||
{
|
||||
xor_b(0x08, &PADRH); /* Set PR active, independent of polarity */
|
||||
|
||||
delay = 0;
|
||||
delay = 100;
|
||||
while (PBDRH & 0x80) /* Wait until /RTW becomes active */
|
||||
{
|
||||
delay++;
|
||||
if (delay > 100) /* Bail out if we have to wait too long */
|
||||
if (--delay <= 0) /* Bail out if we have to wait too long */
|
||||
{ /* i.e. the MAS doesn't want to talk to us */
|
||||
xor_b(0x08, &PADRH); /* Set PR inactive */
|
||||
goto transfer_end; /* and get out of here */
|
||||
|
|
@ -613,9 +620,10 @@ void rec_tick(void)
|
|||
if(num_bytes < 0)
|
||||
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);
|
||||
wake_up_thread();
|
||||
}
|
||||
|
|
@ -998,13 +1006,11 @@ static void mpeg_thread(void)
|
|||
int start_offset;
|
||||
#if CONFIG_HWCODEC == MAS3587F
|
||||
int amount_to_save;
|
||||
int writelen;
|
||||
int framelen;
|
||||
unsigned long saved_header = 0;
|
||||
int startpos;
|
||||
int save_endpos = 0;
|
||||
int rc;
|
||||
long offset;
|
||||
int countdown;
|
||||
#endif /* #if CONFIG_HWCODEC == MAS3587F */
|
||||
|
||||
is_playing = false;
|
||||
|
|
@ -1640,7 +1646,7 @@ static void mpeg_thread(void)
|
|||
/* Wait until at least one frame is encoded and get the
|
||||
frame header, for later use by the Xing header
|
||||
generation */
|
||||
sleep(HZ/10);
|
||||
sleep(HZ/5);
|
||||
saved_header = mpeg_get_last_header();
|
||||
|
||||
/* delayed until buffer is saved, don't open yet */
|
||||
|
|
@ -1655,7 +1661,8 @@ static void mpeg_thread(void)
|
|||
stop_recording();
|
||||
|
||||
/* 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);
|
||||
break;
|
||||
|
||||
|
|
@ -1720,56 +1727,58 @@ static void mpeg_thread(void)
|
|||
break;
|
||||
|
||||
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
|
||||
in the buffer. If we haven't recorded a single
|
||||
frame within 200ms, the MAS is probably not recording
|
||||
anything, and we bail out. */
|
||||
countdown = 20;
|
||||
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();
|
||||
}
|
||||
|
||||
if (amount_to_save >= 1800)
|
||||
{
|
||||
/* Now find a frame boundary to split at */
|
||||
startpos = audiobuf_write - 1800;
|
||||
if(startpos < 0)
|
||||
startpos += audiobuflen;
|
||||
save_endpos = audiobuf_write - 1800;
|
||||
if (save_endpos < 0)
|
||||
save_endpos += audiobuflen;
|
||||
|
||||
rc = mem_find_next_frame(startpos, &offset, 1800,
|
||||
rc = mem_find_next_frame(save_endpos, &offset, 1800,
|
||||
saved_header);
|
||||
if (rc) /* Header found? */
|
||||
{
|
||||
/* offset will now contain the number of bytes to
|
||||
add to startpos to find the frame boundary */
|
||||
startpos += offset;
|
||||
if(startpos >= audiobuflen)
|
||||
startpos -= audiobuflen;
|
||||
save_endpos += offset;
|
||||
if (save_endpos >= audiobuflen)
|
||||
save_endpos -= audiobuflen;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No header found. Let's save the whole buffer. */
|
||||
startpos = audiobuf_write;
|
||||
save_endpos = audiobuf_write;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Too few bytes recorded, timeout */
|
||||
startpos = audiobuf_write;
|
||||
save_endpos = audiobuf_write;
|
||||
}
|
||||
|
||||
amount_to_save = startpos - audiobuf_read;
|
||||
if(amount_to_save < 0)
|
||||
amount_to_save += audiobuflen;
|
||||
saving_status = NEW_FILE;
|
||||
queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0);
|
||||
|
||||
/* First save up to the end of the buffer */
|
||||
writelen = MIN(amount_to_save,
|
||||
audiobuflen - audiobuf_read);
|
||||
case MPEG_SAVE_DATA:
|
||||
if (saving_status == BUFFER_FULL)
|
||||
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);
|
||||
|
||||
|
|
@ -1777,103 +1786,20 @@ static void mpeg_thread(void)
|
|||
panicf("recfile: %d", mpeg_file);
|
||||
}
|
||||
|
||||
if(writelen)
|
||||
{
|
||||
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 */
|
||||
amount_to_save = save_endpos - audiobuf_read;
|
||||
if (amount_to_save < 0)
|
||||
{
|
||||
amount_to_save += audiobuflen;
|
||||
}
|
||||
|
||||
DEBUGF("r: %x w: %x\n", audiobuf_read, audiobuf_write);
|
||||
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,
|
||||
amount_to_save = MIN(amount_to_save,
|
||||
audiobuflen - audiobuf_read);
|
||||
|
||||
DEBUGF("wrl: %x\n", writelen);
|
||||
|
||||
if (mpeg_file < 0) /* delayed file opening */
|
||||
{
|
||||
mpeg_file = open(delayed_filename,
|
||||
O_WRONLY|O_CREAT);
|
||||
|
||||
if(mpeg_file < 0)
|
||||
panicf("recfile: %d", mpeg_file);
|
||||
}
|
||||
|
||||
#if MEM == 8
|
||||
amount_to_save = MIN(0x100000, amount_to_save);
|
||||
#endif
|
||||
#ifdef HAVE_MMC /* MMC is slow, so don't save too large chunks at once */
|
||||
amount_to_save = MIN(0x40000, amount_to_save);
|
||||
#endif
|
||||
rc = write(mpeg_file, audiobuf + audiobuf_read,
|
||||
writelen);
|
||||
|
||||
amount_to_save);
|
||||
if (rc < 0)
|
||||
{
|
||||
if (errno == ENOSPC)
|
||||
|
|
@ -1881,39 +1807,52 @@ static void mpeg_thread(void)
|
|||
mpeg_errno = AUDIOERR_DISK_FULL;
|
||||
stop_recording();
|
||||
queue_post(&mpeg_queue, MPEG_STOP_DONE, 0);
|
||||
/* will close the file */
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
panicf("rec wrt: %d", rc);
|
||||
}
|
||||
}
|
||||
|
||||
audiobuf_read += amount_to_save;
|
||||
if (audiobuf_read >= audiobuflen)
|
||||
audiobuf_read = 0;
|
||||
|
||||
if (audiobuf_read == save_endpos) /* all saved */
|
||||
{
|
||||
switch (saving_status)
|
||||
{
|
||||
case BUFFER_FULL:
|
||||
rc = fsync(mpeg_file);
|
||||
if (rc < 0)
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
||||
case MPEG_INIT_PLAYBACK:
|
||||
|
|
@ -2200,8 +2139,7 @@ static void start_prerecording(void)
|
|||
} while(val & 1);
|
||||
|
||||
is_recording = true;
|
||||
stop_pending = false;
|
||||
saving = false;
|
||||
saving_status = NOT_SAVING;
|
||||
|
||||
demand_irq_enable(true);
|
||||
}
|
||||
|
|
@ -2235,8 +2173,7 @@ static void start_recording(void)
|
|||
}
|
||||
|
||||
is_recording = true;
|
||||
stop_pending = false;
|
||||
saving = false;
|
||||
saving_status = NOT_SAVING;
|
||||
paused = false;
|
||||
|
||||
/* Store the current time */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue