From ba966c17614fc2de2b443a82071415b194753aa4 Mon Sep 17 00:00:00 2001 From: Jens Arnold Date: Thu, 15 Sep 2005 05:29:26 +0000 Subject: Archos recording/playback: (1) Xing header creation: * Now estimates the framecount from the recording time if the MAS frame counter saturates, so it always writes a valid Xing header. * Reverted my brainless short-xing-header change. This was only valid for layer 3. (2) Xing/VBRI header evaluation: Fixed possible overflow in bitrate calculation. (3) MPEG thread: Avoid double chunk size limiting for the rare case of 8 MB modded Ondios. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7522 a1c6a512-1295-4272-9138-f99709370657 --- apps/codecs.h | 10 ++--- apps/plugin.h | 10 ++--- apps/plugins/vbrfix.c | 2 +- firmware/export/mp3data.h | 11 +++--- firmware/export/mpeg.h | 2 +- firmware/mp3data.c | 96 ++++++++++++++++++++++++++--------------------- firmware/mpeg.c | 35 ++++++++--------- 7 files changed, 89 insertions(+), 77 deletions(-) diff --git a/apps/codecs.h b/apps/codecs.h index 517d68ba03..d7242a8330 100644 --- a/apps/codecs.h +++ b/apps/codecs.h @@ -79,12 +79,12 @@ #endif /* increase this every time the api struct changes */ -#define CODEC_API_VERSION 43 +#define CODEC_API_VERSION 44 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define CODEC_MIN_API_VERSION 43 +#define CODEC_MIN_API_VERSION 44 /* codec return codes */ enum codec_status { @@ -303,9 +303,9 @@ struct codec_api { bool (*mp3info)(struct mp3entry *entry, const char *filename, bool v1first); int (*count_mp3_frames)(int fd, int startpos, int filesize, void (*progressfunc)(int)); - int (*create_xing_header)(int fd, int startpos, int filesize, - unsigned char *buf, int num_frames, - unsigned long header_template, + int (*create_xing_header)(int fd, long startpos, long filesize, + unsigned char *buf, unsigned long num_frames, + unsigned long rec_time, unsigned long header_template, void (*progressfunc)(int), bool generate_toc); unsigned long (*find_next_frame)(int fd, long *offset, long max_offset, unsigned long last_header); diff --git a/apps/plugin.h b/apps/plugin.h index 01ace98690..5cc9c1b58a 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -89,12 +89,12 @@ #endif /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 50 +#define PLUGIN_API_VERSION 51 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 50 +#define PLUGIN_MIN_API_VERSION 51 /* plugin return codes */ enum plugin_status { @@ -404,9 +404,9 @@ struct plugin_api { bool (*mp3info)(struct mp3entry *entry, const char *filename, bool v1first); int (*count_mp3_frames)(int fd, int startpos, int filesize, void (*progressfunc)(int)); - int (*create_xing_header)(int fd, int startpos, int filesize, - unsigned char *buf, int num_frames, - unsigned long header_template, + int (*create_xing_header)(int fd, long startpos, long filesize, + unsigned char *buf, unsigned long num_frames, + unsigned long rec_time, unsigned long header_template, void (*progressfunc)(int), bool generate_toc); unsigned long (*find_next_frame)(int fd, long *offset, long max_offset, unsigned long last_header); diff --git a/apps/plugins/vbrfix.c b/apps/plugins/vbrfix.c index b821b9c5fa..ca6b9ca096 100644 --- a/apps/plugins/vbrfix.c +++ b/apps/plugins/vbrfix.c @@ -168,7 +168,7 @@ static bool vbr_fix(char *selected_file) /* Note: We don't need to pass a template header because it will be taken from the mpeg stream */ framelen = rb->create_xing_header(fd, entry.first_frame_offset, - flen, xingbuf, num_frames, + flen, xingbuf, num_frames, 0, 0, xingupdate, true); /* Try to fit the Xing header first in the stream. Replace the existing diff --git a/firmware/export/mp3data.h b/firmware/export/mp3data.h index 3961664815..f36120a4e5 100644 --- a/firmware/export/mp3data.h +++ b/firmware/export/mp3data.h @@ -59,17 +59,18 @@ struct mp3info { #define VBR_TOC_FLAG 0x04 #define VBR_QUALITY_FLAG 0x08 -#define MAX_XING_HEADER_SIZE 288 +#define MAX_XING_HEADER_SIZE 576 -unsigned long find_next_frame(int fd, long *offset, long max_offset, unsigned long last_header); +unsigned long find_next_frame(int fd, long *offset, long max_offset, + unsigned long last_header); unsigned long mem_find_next_frame(int startpos, long *offset, long max_offset, unsigned long last_header); int get_mp3file_info(int fd, struct mp3info *info); int count_mp3_frames(int fd, int startpos, int filesize, void (*progressfunc)(int)); -int create_xing_header(int fd, int startpos, int filesize, - unsigned char *buf, int num_frames, - unsigned long header_template, +int create_xing_header(int fd, long startpos, long filesize, + unsigned char *buf, unsigned long num_frames, + unsigned long rec_time, unsigned long header_template, void (*progressfunc)(int), bool generate_toc); #endif diff --git a/firmware/export/mpeg.h b/firmware/export/mpeg.h index a0ad3c405c..a7c9cac321 100644 --- a/firmware/export/mpeg.h +++ b/firmware/export/mpeg.h @@ -41,7 +41,7 @@ #define MPEG_MAX_PRERECORD_SECONDS 30 /* For ID3 info and VBR header */ -#define MPEG_RESERVED_HEADER_SPACE (4096 + 288) +#define MPEG_RESERVED_HEADER_SPACE (4096 + 576) #if (CONFIG_CODEC == MAS3587F) || defined(SIMULATOR) void mpeg_init_recording(void); diff --git a/firmware/mp3data.c b/firmware/mp3data.c index c2d4cd2c37..519bc60b04 100644 --- a/firmware/mp3data.c +++ b/firmware/mp3data.c @@ -406,7 +406,7 @@ int get_mp3file_info(int fd, struct mp3info *info) /* Is it a VBR file? */ info->is_vbr = info->is_xing_vbr = !memcmp(vbrheader, "Xing", 4); - if(vbrheader[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */ + if (vbrheader[7] & VBR_FRAMES_FLAG) /* Is the frame count there? */ { info->frame_count = BYTES2INT(vbrheader[i], vbrheader[i+1], vbrheader[i+2], vbrheader[i+3]); @@ -417,19 +417,24 @@ int get_mp3file_info(int fd, struct mp3info *info) i += 4; } - if(vbrheader[7] & VBR_BYTES_FLAG) /* Is byte count there? */ + if (vbrheader[7] & VBR_BYTES_FLAG) /* Is byte count there? */ { info->byte_count = BYTES2INT(vbrheader[i], vbrheader[i+1], vbrheader[i+2], vbrheader[i+3]); i += 4; } - if(info->file_time && info->byte_count) - info->bitrate = info->byte_count * 8 / info->file_time; + if (info->file_time && info->byte_count) + { + if (info->byte_count <= (ULONG_MAX/8)) + info->bitrate = info->byte_count * 8 / info->file_time; + else + info->bitrate = info->byte_count / (info->file_time >> 3); + } else info->bitrate = 0; - - if(vbrheader[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */ + + if (vbrheader[7] & VBR_TOC_FLAG) /* Is table-of-contents there? */ { memcpy( info->toc, vbrheader+i, 100 ); i += 100; @@ -492,7 +497,11 @@ int get_mp3file_info(int fd, struct mp3info *info) info->file_time = info->frame_count * info->ft_num / info->ft_den; else info->file_time = info->frame_count / info->ft_den * info->ft_num; - info->bitrate = info->byte_count * 8 / info->file_time; + + if (info->byte_count <= (ULONG_MAX/8)) + info->bitrate = info->byte_count * 8 / info->file_time; + else + info->bitrate = info->byte_count / (info->file_time >> 3); /* We don't parse the TOC, since we don't yet know how to (FIXME) */ num_offsets = BYTES2INT(0, 0, vbrheader[18], vbrheader[19]); @@ -587,37 +596,35 @@ int count_mp3_frames(int fd, int startpos, int filesize, static const char cooltext[] = "Rockbox - rocks your box"; -int create_xing_header(int fd, int startpos, int filesize, - unsigned char *buf, /* must be at least 288 bytes */ - int num_frames, unsigned long header_template, +/* buf needs to be the audio buffer with TOC generation enabled, + and at least MAX_XING_HEADER_SIZE bytes otherwise */ +int create_xing_header(int fd, long startpos, long filesize, + unsigned char *buf, unsigned long num_frames, + unsigned long rec_time, unsigned long header_template, void (*progressfunc)(int), bool generate_toc) -{ - unsigned long header = 0; +{ struct mp3info info; - int pos, last_pos; - int i, j; - long bytes; + unsigned char toc[100]; + unsigned long header = 0; + unsigned long xing_header_template = header_template; unsigned long filepos; - int x; + long pos, last_pos; + long j; + long bytes; + int i; int index; - unsigned char toc[100]; - unsigned long xing_header_template = 0; DEBUGF("create_xing_header()\n"); - if(header_template) - xing_header_template = header_template; - if(generate_toc) { lseek(fd, startpos, SEEK_SET); buf_init(); - + /* Generate filepos table */ last_pos = 0; filepos = 0; header = 0; - x = 0; for(i = 0;i < 100;i++) { /* Calculate the absolute frame number for this seek point */ pos = i * num_frames / 100; @@ -654,7 +661,7 @@ int create_xing_header(int fd, int startpos, int filesize, * the upper 8 bits of the file position are nonzero * (i.e. files over 16mb in size). */ - if (filepos > 0xFFFFFF) + if (filepos > (ULONG_MAX/256)) { /* instead of multiplying filepos by 256, we divide * filesize by 256. @@ -673,46 +680,49 @@ int create_xing_header(int fd, int startpos, int filesize, } } - /* Check the template header for validity and get some preliminary info. */ - if (!mp3headerinfo(&info, xing_header_template)) + /* Use the template header and create a new one. + We ignore the Protection bit even if the rest of the stream is + protected. */ + header = xing_header_template & ~(BITRATE_MASK|PROTECTION_MASK|PADDING_MASK); + header |= 8 << 12; /* This gives us plenty of space, 192..576 bytes */ + + if (!mp3headerinfo(&info, header)) return 0; /* invalid header */ + if (num_frames == 0 && rec_time) { + /* estimate the number of frames based on the recording time */ + if (rec_time <= ULONG_MAX / info.ft_den) + num_frames = rec_time * info.ft_den / info.ft_num; + else + num_frames = rec_time / info.ft_num * info.ft_den; + } + /* Clear the frame */ memset(buf, 0, MAX_XING_HEADER_SIZE); - /* Use the template header and create a new one. */ - header = xing_header_template & ~(BITRATE_MASK | PROTECTION_MASK); + /* Write the header to the buffer */ + long2bytes(buf, header); - /* Calculate position of VBR header and required frame bitrate */ + /* Calculate position of VBR header */ if (info.version == MPEG_VERSION1) { - header |= 5 << 12; if (info.channel_mode == 3) /* mono */ index = 21; else index = 36; } else { - if (info.version == MPEG_VERSION2) - header |= 8 << 12; - else /* MPEG_VERSION2_5 */ - header |= 4 << 12; if (info.channel_mode == 3) /* mono */ index = 13; else index = 21; } - mp3headerinfo(&info, header); /* Get final header info */ - /* Size is now always one of 192, 208 or 288 bytes */ - - /* Write the header to the buffer */ - long2bytes(buf, header); /* Create the Xing data */ memcpy(&buf[index], "Xing", 4); - long2bytes(&buf[index+4], ((num_frames?VBR_FRAMES_FLAG:0) | - (filesize?VBR_BYTES_FLAG:0) | - (generate_toc?VBR_TOC_FLAG:0))); - index = index+8; + long2bytes(&buf[index+4], (num_frames ? VBR_FRAMES_FLAG : 0) + | (filesize ? VBR_BYTES_FLAG : 0) + | (generate_toc ? VBR_TOC_FLAG : 0)); + index += 8; if(num_frames) { long2bytes(&buf[index], num_frames); diff --git a/firmware/mpeg.c b/firmware/mpeg.c index 8d2d13b6ee..9d1aa3b9d6 100644 --- a/firmware/mpeg.c +++ b/firmware/mpeg.c @@ -190,6 +190,7 @@ 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 unsigned long last_rec_time; static unsigned long num_rec_bytes; static unsigned long last_rec_bytes; static unsigned long frame_count_start; @@ -1611,12 +1612,11 @@ 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 - amount_to_read = MIN(0x100000, amount_to_read); -#endif /* MEM == 8 */ #ifdef HAVE_MMC /* MMC is slow, so don't read too large chunks */ amount_to_read = MIN(0x40000, amount_to_read); -#endif /* HAVE_MMC */ +#elif MEM == 8 + amount_to_read = MIN(0x100000, amount_to_read); +#endif /* Read as much mpeg data as we can fit in the buffer */ if(mpeg_file >= 0) @@ -1810,7 +1810,7 @@ static void mpeg_thread(void) DEBUGF("MPEG_STOP\n"); stop_recording(); - + /* Save the remaining data in the buffer */ save_endpos = audiobuf_write; saving_status = STOP_RECORDING; @@ -1848,7 +1848,7 @@ static void mpeg_thread(void) case MPEG_NEW_FILE: /* Bail out when a more important save is happening */ - if (saving_status > NEW_FILE) + if (saving_status > NEW_FILE) break; /* Make sure we have at least one complete frame @@ -1865,6 +1865,11 @@ static void mpeg_thread(void) mas_readmem(MAS_BANK_D0, MAS_D0_MPEG_FRAME_COUNT, &frame_count_end, 1); + last_rec_time = current_tick - record_start_time; + record_start_time = current_tick; + if (paused) + pause_start_time = record_start_time; + /* capture all values at one point */ level = set_irq_level(HIGHEST_IRQ_LEVEL); save_endpos = audiobuf_write; @@ -1916,11 +1921,10 @@ static void mpeg_thread(void) 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); +#elif MEM == 8 + amount_to_save = MIN(0x100000, amount_to_save); #endif rc = write(mpeg_file, audiobuf + audiobuf_read, amount_to_save); @@ -2210,7 +2214,7 @@ void mpeg_record(const char *filename) strncpy(recording_filename, filename, MAX_PATH - 1); recording_filename[MAX_PATH - 1] = 0; - + queue_post(&mpeg_queue, MPEG_RECORD, NULL); } @@ -2274,7 +2278,8 @@ static void update_header(void) /* saved_header is saved right before stopping the MAS */ framelen = create_xing_header(fd, 0, last_rec_bytes, xing_buffer, - frames, saved_header, NULL, false); + frames, last_rec_time * (1000/HZ), + saved_header, NULL, false); lseek(fd, MPEG_RESERVED_HEADER_SPACE - framelen, SEEK_SET); write(fd, xing_buffer, framelen); @@ -2350,7 +2355,7 @@ static void start_recording(void) record_start_time = current_tick; pause_start_time = 0; - + demand_irq_enable(true); } @@ -2398,6 +2403,7 @@ static void stop_recording(void) last_rec_bytes = num_rec_bytes; mas_readmem(MAS_BANK_D0, MAS_D0_MPEG_FRAME_COUNT, &frame_count_end, 1); + last_rec_time = current_tick - record_start_time; /* Start monitoring */ shadow_io_control_main |= (1 << 10); @@ -2524,11 +2530,6 @@ void mpeg_new_file(const char *filename) strncpy(recording_filename, filename, MAX_PATH - 1); recording_filename[MAX_PATH - 1] = 0; - /* Store the current time */ - record_start_time = current_tick; - if(paused) - pause_start_time = record_start_time; - queue_post(&mpeg_queue, MPEG_NEW_FILE, NULL); } -- cgit