diff options
author | Jens Arnold <amiconn@rockbox.org> | 2005-06-04 11:29:39 +0000 |
---|---|---|
committer | Jens Arnold <amiconn@rockbox.org> | 2005-06-04 11:29:39 +0000 |
commit | e03f40284ed98c4fab5f9ec39db4a521ce4a061a (patch) | |
tree | 9581de671bd1f41112ad713432731b59aee57620 /firmware | |
parent | d9b66127acd955c2a29b03165ced3aa17cb7a4a4 (diff) | |
download | rockbox-e03f40284ed98c4fab5f9ec39db4a521ce4a061a.tar.gz rockbox-e03f40284ed98c4fab5f9ec39db4a521ce4a061a.zip |
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
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/mpeg.c | 303 |
1 files changed, 120 insertions, 183 deletions
diff --git a/firmware/mpeg.c b/firmware/mpeg.c index 28bfa6201e..211f79201d 100644 --- a/firmware/mpeg.c +++ b/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 */ @@ -586,7 +593,7 @@ void rec_tick(void) #endif /* #ifdef DEBUG */ num_rec_bytes += i; - + if(is_prerecording) { if(TIME_AFTER(current_tick, prerecord_timeout)) @@ -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,15 +1006,13 @@ 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; play_pending = false; playing = false; @@ -1423,7 +1429,7 @@ static void mpeg_thread(void) /* Don't read more than until the end of the buffer */ amount_to_read = MIN(audiobuflen - audiobuf_write, amount_to_read); -#if MEM == 8 +#if MEM == 8 amount_to_read = MIN(0x100000, amount_to_read); #endif /* #if MEM == 8 */ #ifdef HAVE_MMC /* MMC is slow, so don't read too large chunks */ @@ -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 */ @@ -1648,39 +1654,40 @@ static void mpeg_thread(void) mpeg_file = -1; break; - + case MPEG_STOP: DEBUGF("MPEG_STOP\n"); 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; - + case MPEG_STOP_DONE: DEBUGF("MPEG_STOP_DONE\n"); - - if(mpeg_file >= 0) + + if (mpeg_file >= 0) close(mpeg_file); - if(!disable_xing_header && num_rec_bytes > 0) + if (!disable_xing_header && num_rec_bytes > 0) { /* Create the Xing header */ mpeg_file = open(recording_filename, O_RDWR); - if(mpeg_file < 0) + if (mpeg_file < 0) panicf("rec upd: %d (%s)", mpeg_file, recording_filename); /* If the number of recorded frames have reached 0x7ffff, we can no longer trust it */ - if(num_recorded_frames == 0x7ffff) + if (num_recorded_frames == 0x7ffff) num_recorded_frames = 0; /* Also, if we have been prerecording, the frame count will be wrong */ - if(prerecording) + if (prerecording) num_recorded_frames = 0; /* saved_header is saved right before stopping @@ -1697,7 +1704,7 @@ static void mpeg_thread(void) close(mpeg_file); } mpeg_file = -1; - + #ifdef DEBUG1 { int i; @@ -1712,7 +1719,7 @@ static void mpeg_thread(void) } #endif /* #ifdef DEBUG1 */ - if(prerecording) + if (prerecording) { start_prerecording(); } @@ -1720,202 +1727,134 @@ 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) + 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? */ + 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; - /* First save up to the end of the buffer */ - writelen = MIN(amount_to_save, - audiobuflen - audiobuf_read); - - if (mpeg_file < 0) /* delayed file opening */ + saving_status = NEW_FILE; + queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); + + case MPEG_SAVE_DATA: + if (saving_status == BUFFER_FULL) + save_endpos = audiobuf_write; + + if (mpeg_file < 0) /* delayed file open */ { mpeg_file = open(delayed_filename, O_WRONLY|O_CREAT); - - if(mpeg_file < 0) + + if (mpeg_file < 0) 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); - } - } - } + amount_to_save = save_endpos - audiobuf_read; + if (amount_to_save < 0) + amount_to_save += audiobuflen; - /* Then save the rest */ - writelen = amount_to_save - writelen; - if(writelen) + amount_to_save = MIN(amount_to_save, + audiobuflen - audiobuf_read); +#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, + amount_to_save); + if (rc < 0) { - rc = write(mpeg_file, audiobuf, writelen); - if(rc < 0) + if (errno == ENOSPC) { - 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); - } + 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); } - /* 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) - { - amount_to_save += audiobuflen; - } - - DEBUGF("r: %x w: %x\n", audiobuf_read, audiobuf_write); - DEBUGF("ats: %x\n", amount_to_save); + if (audiobuf_read >= audiobuflen) + audiobuf_read = 0; - /* Save data only if the buffer is getting full, - or if we should stop recording */ - if(amount_to_save) + if (audiobuf_read == save_endpos) /* all saved */ { - if(audiobuflen - - amount_to_save < MPEG_RECORDING_LOW_WATER || - stop_pending) + switch (saving_status) { - /* Only save up to the end of the buffer */ - writelen = 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); - } + case BUFFER_FULL: + rc = fsync(mpeg_file); + if (rc < 0) + panicf("rec fls: %d", rc); + ata_sleep(); + break; - rc = write(mpeg_file, audiobuf + audiobuf_read, - writelen); - - if(rc < 0) - { - if(errno == ENOSPC) - { - mpeg_errno = AUDIOERR_DISK_FULL; - stop_recording(); - queue_post(&mpeg_queue, MPEG_STOP_DONE, 0); - break; - } - else - { - panicf("rec wrt: %d", rc); - } - } - - audiobuf_read += amount_to_save; - if(audiobuf_read >= audiobuflen) - audiobuf_read = 0; - - rc = fsync(mpeg_file); - if(rc < 0) - panicf("rec fls: %d", rc); + 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; - queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); - } - else - { - saving = false; - ata_sleep(); + case STOP_RECORDING: + queue_post(&mpeg_queue, MPEG_STOP_DONE, NULL); + /* will close the file */ + break; + + default: + break; } + saving_status = NOT_SAVING; } - 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(); - } + else /* tell ourselves to save the next chunk */ + queue_post(&mpeg_queue, MPEG_SAVE_DATA, 0); + break; - + case MPEG_INIT_PLAYBACK: /* Stop the prerecording */ stop_recording(); @@ -1924,12 +1863,12 @@ static void mpeg_thread(void) break; case MPEG_PAUSE_RECORDING: - pause_recording(); - break; + pause_recording(); + break; case MPEG_RESUME_RECORDING: - resume_recording(); - break; + resume_recording(); + break; case SYS_USB_CONNECTED: /* We can safely go to USB mode if no recording @@ -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 */ |