From c4146cc00a9749574213b2df71691a4ce94aebfe Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Wed, 24 Sep 2003 16:40:20 +0000 Subject: [PATCH] Rewrote the complete MP3 handling. ID3 tags and all MPEG audio files should be handled correctly now. --- ChangeLog | 7 +++ src/mkvmerge.cpp | 7 ++- src/mmg/tab_input.cpp | 10 ++-- src/mp3_common.cpp | 39 ++++++-------- src/p_mp3.cpp | 52 +++++++++---------- src/p_mp3.h | 1 - src/r_mp3.cpp | 117 ++++++++++++++++++++---------------------- src/r_mp3.h | 2 +- 8 files changed, 112 insertions(+), 123 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3485749bd..f0366a96a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2003-09-24 Moritz Bunkus + + * mkvmerge: bug fix/new feature: Rewrote the complete MP3 + handling. Now files with ID3 tags (both v1 and v2) are handled + correctly. All MPEG-1 audio files (all layers) should be handled + correctly now. + 2003-09-23 Moritz Bunkus * mkvextract: new feature: Support for extract HE-AAC tracks to diff --git a/src/mkvmerge.cpp b/src/mkvmerge.cpp index bfc28453a..541fd4e4a 100644 --- a/src/mkvmerge.cpp +++ b/src/mkvmerge.cpp @@ -194,6 +194,7 @@ file_type_t file_types[] = {"ac3", TYPEAC3, "A/52 (aka AC3)"}, {"avi", TYPEAVI, "AVI (Audio/Video Interleaved)"}, {"dts", TYPEDTS, "DTS (Digital Theater System)"}, + {"mp2", TYPEMP3, "MPEG1 layer II audio (CBR and VBR/ABR)"}, {"mp3", TYPEMP3, "MPEG1 layer III audio (CBR and VBR/ABR)"}, {"mkv", TYPEMATROSKA, "general Matroska files"}, #ifdef HAVE_OGGVORBIS @@ -1033,7 +1034,7 @@ static void identify(const char *filename) { files.push_back(file); - verbose = 0; + verbose--; identifying = true; create_readers(); @@ -1077,11 +1078,13 @@ static void parse_args(int argc, char **argv) { // Check if only information about the file is wanted. In this mode only // two parameters are allowed: the --identify switch and the file. - if ((argc == 2) && + if (((argc == 2) || (argc == 3)) && (!strcmp(argv[0], "-i") || !strcmp(argv[0], "--identify") || !strcmp(argv[0], "--identify-verbose"))) { if (!strcmp(argv[0], "--identify-verbose")) identify_verbose = true; + if (argc == 3) + verbose = 3; identify(argv[1]); mxexit(); } diff --git a/src/mmg/tab_input.cpp b/src/mmg/tab_input.cpp index 5165afcfb..06878efa1 100644 --- a/src/mmg/tab_input.cpp +++ b/src/mmg/tab_input.cpp @@ -293,18 +293,18 @@ void tab_input::on_add_file(wxCommandEvent &evt) { unsigned int i; wxFileDialog dlg(NULL, "Choose an input file", last_open_dir, "", - _T("Media files (*.aac;*.ac3;*.ass;*.avi;*.dts;*.idx;*.mp3;" - "*.mka;" + _T("Media files (*.aac;*.ac3;*.ass;*.avi;*.dts;*.idx;*.mp2;" + "*.mp3;*.mka;" "*.mkv;*.mov;*.mp4;*.ogm;*.ogg;*.rm;*.rmvb;*.srt;*.ssa;" "*.wav)|" - "*.aac;*.ac3;*.ass;*.avi;*.dts;*.idx;*.mp3;*.mka;*.mkv;" - "*.mov;" + "*.aac;*.ac3;*.ass;*.avi;*.dts;*.idx;*.mp2;*.mp3;*.mka;" + "*.mkv;*.mov;" "*.mp4;*.ogm;*.ogg;*.rm;*.rmvb;*.srt;*.ssa;*.wav|" "AAC (Advanced Audio Coding) (*.aac;*.mp4)|*.aac;*.mp4|" "A/52 (aka AC3) (*.ac3)|*.ac3|" "AVI (Audio/Video Interleaved) (*.avi)|*.avi|" "DTS (Digital Theater System) (*.dts)|*.dts|" - "MPEG1 layer III audio (*.mp3)|*.mp3|" + "MPEG audio files (*.mp2;*.mp3)|*.mp2;*.mp3|" "Matroska A/V files (*.mka;*.mkv)|*.mka;*.mkv|" "QuickTime/MP4 A/V (*.mov;*.mp4)|*.mov;*.mp4|" "Audio/Video embedded in OGG (*.ogg;*.ogm)|*.ogg;*.ogm|" diff --git a/src/mp3_common.cpp b/src/mp3_common.cpp index a6d0bef04..291c44b66 100644 --- a/src/mp3_common.cpp +++ b/src/mp3_common.cpp @@ -134,7 +134,7 @@ static int mp3_samples_per_channel[3][3] = { int find_mp3_header(unsigned char *buf, int size) { int i, pos; - unsigned long header, id3_size; + unsigned long header; if (size < 4) return -1; @@ -144,21 +144,10 @@ int find_mp3_header(unsigned char *buf, int size) { if ((pos + 10) >= size) return -1; - for (i = 6, id3_size = 0; i < 10; i++) { - id3_size <<= 8; - id3_size |= buf[i + pos]; - } - if ((pos + id3_size) >= size) - return -1; - return pos; } - if ((buf[pos] == 'T') && (buf[pos + 1] == 'A') && (buf[pos + 2] == 'G')) { - if ((pos + 128) >= size) - return -1; - + if ((buf[pos] == 'T') && (buf[pos + 1] == 'A') && (buf[pos + 2] == 'G')) return pos; - } for (i = 0, header = 0; i < 4; i++) { header <<= 8; @@ -194,12 +183,19 @@ void decode_mp3_header(unsigned char *buf, mp3_header_t *h) { if ((buf[0] == 'I') && (buf[1] == 'D') && (buf[2] == '3')) { h->is_tag = true; - h->framesize = (((uint32_t)buf[6]) << 24) | (((uint32_t)buf[7]) << 16) | - (((uint32_t)buf[8]) << 8) | ((uint32_t)buf[9]); + h->framesize = 0; + for (i = 6; i < 10; i++) { + h->framesize <<= 7; + h->framesize |= ((uint32_t)buf[i]) & 0x7f; + } + h->framesize += 10; + if ((buf[3] >= 4) && ((buf[5] & 0x10) == 0x10)) + h->framesize += 10; + return; } - if ((buf[0] == 'T') && (buf[1] == 'A') && (buf[3] == 'G')) { + if ((buf[0] == 'T') && (buf[1] == 'A') && (buf[2] == 'G')) { h->is_tag = true; h->framesize = 128; return; @@ -254,19 +250,16 @@ void decode_mp3_header(unsigned char *buf, mp3_header_t *h) { if (h->layer == 3) { if (h->version == 1) - h->framesize = 144000 * h->bitrate / h->sampling_frequency + - h->padding - 4; + h->framesize = 144000 * h->bitrate / h->sampling_frequency + h->padding; else - h->framesize = 72000 * h->bitrate / h->sampling_frequency + - h->padding - 4; + h->framesize = 72000 * h->bitrate / h->sampling_frequency + h->padding; } else if (h->layer == 2) - h->framesize = 144000 * h->bitrate / h->sampling_frequency + - h->padding - 4; + h->framesize = 144000 * h->bitrate / h->sampling_frequency + h->padding; else h->framesize = (12000 * h->bitrate / h->sampling_frequency + - h->padding) * 4 - 4; + h->padding) * 4; h->samples_per_channel = mp3_samples_per_channel[h->version - 1][h->layer - 1]; diff --git a/src/p_mp3.cpp b/src/p_mp3.cpp index 1b588ce38..6ff9a56d9 100644 --- a/src/p_mp3.cpp +++ b/src/p_mp3.cpp @@ -66,30 +66,14 @@ void mp3_packetizer_c::add_to_buffer(unsigned char *buf, int size) { buffer_size += size; } -int mp3_packetizer_c::mp3_packet_available() { - int pos; - mp3_header_t mp3header; - - if (packet_buffer == NULL) - return 0; - pos = find_mp3_header(packet_buffer, buffer_size); - if (pos < 0) - return 0; - decode_mp3_header(&packet_buffer[pos], &mp3header); - if ((pos + mp3header.framesize + 4) > buffer_size) - return 0; - - return 1; -} - void mp3_packetizer_c::remove_mp3_packet(int pos, int framesize) { int new_size; unsigned char *temp_buf; - new_size = buffer_size - (pos + framesize + 4) + 1; + new_size = buffer_size - (pos + framesize) + 1; temp_buf = (unsigned char *)safemalloc(new_size); if (new_size != 0) - memcpy(temp_buf, &packet_buffer[pos + framesize + 4 - 1], new_size); + memcpy(temp_buf, &packet_buffer[pos + framesize - 1], new_size); safefree(packet_buffer); packet_buffer = temp_buf; buffer_size = new_size; @@ -102,10 +86,21 @@ unsigned char *mp3_packetizer_c::get_mp3_packet(mp3_header_t *mp3header) { if (packet_buffer == NULL) return 0; - pos = find_mp3_header(packet_buffer, buffer_size); - if (pos < 0) - return 0; - decode_mp3_header(&packet_buffer[pos], mp3header); + while (1) { + pos = find_mp3_header(packet_buffer, buffer_size); + if (pos < 0) + return NULL; + decode_mp3_header(&packet_buffer[pos], mp3header); + if ((pos + mp3header->framesize) > buffer_size) + return NULL; + if (!mp3header->is_tag) + break; + + mxverb(2, "mp3_packetizer: Removing TAG packet with size %d\n", + mp3header->framesize); + remove_mp3_packet(pos, mp3header->framesize); + } + if (packetno == 0) { spf = mp3header->samples_per_channel; if (spf != 1152) { @@ -119,8 +114,8 @@ unsigned char *mp3_packetizer_c::get_mp3_packet(mp3_header_t *mp3header) { } } - if ((pos + mp3header->framesize + 4) > buffer_size) - return 0; + if ((pos + mp3header->framesize) > buffer_size) + return NULL; pims = 1000.0 * (float)spf / mp3header->sampling_frequency; @@ -141,8 +136,7 @@ unsigned char *mp3_packetizer_c::get_mp3_packet(mp3_header_t *mp3header) { if ((verbose > 1) && (pos > 1)) mxwarn("mp3_packetizer: skipping %d bytes (no valid MP3 header found).\n", pos); - buf = (unsigned char *)safememdup(packet_buffer + pos, mp3header->framesize - + 4); + buf = (unsigned char *)safememdup(packet_buffer + pos, mp3header->framesize); if (ti->async.displacement > 0) { /* @@ -154,7 +148,7 @@ unsigned char *mp3_packetizer_c::get_mp3_packet(mp3_header_t *mp3header) { ti->async.displacement -= (int)pims; if (ti->async.displacement < (pims / 2)) ti->async.displacement = 0; - memset(buf + 4, 0, mp3header->framesize); + memset(buf + 4, 0, mp3header->framesize - 4); return buf; } @@ -193,14 +187,14 @@ int mp3_packetizer_c::process(unsigned char *buf, int size, packetno++; #ifdef DEBUG - dump_packet(packet, mp3header.framesize + 4); + dump_packet(packet, mp3header.framesize); #endif if (timecode == -1) my_timecode = (int64_t)(1000.0 * old_packetno * spf * ti->async.linear / samples_per_sec); - add_packet(packet, mp3header.framesize + 4, my_timecode, + add_packet(packet, mp3header.framesize, my_timecode, (int64_t)(1000.0 * spf * ti->async.linear / samples_per_sec)); } diff --git a/src/p_mp3.h b/src/p_mp3.h index 6e967313d..fe2179669 100644 --- a/src/p_mp3.h +++ b/src/p_mp3.h @@ -48,7 +48,6 @@ public: private: virtual void add_to_buffer(unsigned char *buf, int size); virtual unsigned char *get_mp3_packet(mp3_header_t *mp3header); - virtual int mp3_packet_available(); virtual void remove_mp3_packet(int pos, int framesize); virtual void dump_debug_info(); diff --git a/src/r_mp3.cpp b/src/r_mp3.cpp index c94353798..cc736db78 100644 --- a/src/r_mp3.cpp +++ b/src/r_mp3.cpp @@ -32,91 +32,84 @@ int mp3_reader_c::probe_file(mm_io_c *mm_io, int64_t size) { unsigned char buf[16384]; int pos, pos2, ptr; - unsigned long header; mp3_header_t mp3header; - if (size > 16384) - size = 16384; try { mm_io->setFilePointer(0, seek_beginning); - if (mm_io->read(buf, size) != size) + + ptr = 0; + while (1) { + mm_io->setFilePointer(ptr, seek_beginning); + size = mm_io->read(buf, 16384); + pos = find_mp3_header(buf, size); + if (pos < 0) + return 0; + decode_mp3_header(&buf[pos], &mp3header); + if (!mp3header.is_tag) + break; + mxverb(2, "mp3_reader: Found tag at %d size %d\n", ptr + pos, + mp3header.framesize); + ptr += pos + mp3header.framesize; + } + + pos2 = find_mp3_header(&buf[pos + mp3header.framesize], + size - pos - mp3header.framesize); + mxverb(2, "mp3_reader: Found first at %d, length %d, version %d, " + "layer %d, second at %d\n", ptr + pos, mp3header.framesize, + mp3header.version, mp3header.layer, pos2); + + if (pos2 != 0) return 0; - mm_io->setFilePointer(0, seek_beginning); + } catch (exception &ex) { return 0; } - ptr = 0; - while (1) { - pos = find_mp3_header(&buf[ptr], size - ptr); - if (pos < 0) - return 0; - decode_mp3_header(&buf[ptr + pos], &mp3header); - if (!mp3header.is_tag) - break; - mxverb(2, "mp3_reader: Found tag at %d size %d\n", ptr + pos, - mp3header.framesize); - ptr += pos + mp3header.framesize; - } - - pos2 = find_mp3_header(&buf[ptr + pos + mp3header.framesize + 4], - size - ptr - pos - 4 - mp3header.framesize); - mxverb(2, "mp3_reader: Found first at %d, length %d, version %d, layer %d, " - "second at %d\n", pos, mp3header.framesize + 4, mp3header.version, - mp3header.layer, pos2); - - if (pos2 != 0) - return 0; - return 1; } mp3_reader_c::mp3_reader_c(track_info_t *nti) throw (error_c): generic_reader_c(nti) { - int pos, this_size, ptr; + int pos, ptr, buf_size; + unsigned char buf[16384]; try { mm_io = new mm_io_c(ti->fname, MODE_READ); - mm_io->setFilePointer(0, seek_end); - size = mm_io->getFilePointer(); - mm_io->setFilePointer(0, seek_beginning); - if (size > 16384) - this_size = 16384; - else - this_size = size; - chunk = (unsigned char *)safemalloc(this_size); - if (mm_io->read(chunk, this_size) != this_size) - throw error_c("mp3_reader: Could not read 4096 bytes."); - mm_io->setFilePointer(0, seek_beginning); + size = mm_io->get_size(); + + ptr = 0; + while (1) { + mm_io->setFilePointer(ptr, seek_beginning); + buf_size = mm_io->read(buf, 16384); + pos = find_mp3_header(buf, buf_size); + if (pos < 0) + throw error_c("Could not find a valid MP3 packet."); + decode_mp3_header(&buf[pos], &mp3header); + if (!mp3header.is_tag) + break; + mxverb(2, "mp3_reader: Found tag at %d size %d\n", ptr + pos, + mp3header.framesize); + ptr += pos + mp3header.framesize; + } + + mm_io->setFilePointer(ptr + pos, seek_beginning); + mxverb(2, "mp3_reader: Found header at %d\n", ptr + pos); + + bytes_processed = 0; + ti->id = 0; // ID for this track. + mp3packetizer = new mp3_packetizer_c(this, mp3header.sampling_frequency, + mp3header.channels, mp3header.layer, + ti); + if (verbose) + mxinfo("Using MP2/MP3 demultiplexer for %s.\n+-> Using " + "MPEG audio output module for audio stream.\n", ti->fname); } catch (exception &ex) { throw error_c("mp3_reader: Could not open the source file."); } - ptr = 0; - while (1) { - pos = find_mp3_header(&chunk[ptr], this_size - ptr); - if (pos < 0) - throw error_c("mp3_reader: No valid MP3 packet found in the first " - "4096 bytes.\n"); - decode_mp3_header(&chunk[ptr + pos], &mp3header); - if (!mp3header.is_tag) - break; - ptr += pos + mp3header.framesize; - } - - bytes_processed = 0; - ti->id = 0; // ID for this track. - mp3packetizer = new mp3_packetizer_c(this, mp3header.sampling_frequency, - mp3header.channels, mp3header.layer, - ti); - if (verbose) - mxinfo("Using MP2/MP3 demultiplexer for %s.\n+-> Using " - "MPEG audio output module for audio stream.\n", ti->fname); } mp3_reader_c::~mp3_reader_c() { delete mm_io; - if (chunk != NULL) - safefree(chunk); if (mp3packetizer != NULL) delete mp3packetizer; } @@ -124,7 +117,7 @@ mp3_reader_c::~mp3_reader_c() { int mp3_reader_c::read(generic_packetizer_c *) { int nread; - nread = mm_io->read(chunk, 4096); + nread = mm_io->read(chunk, 16384); if (nread <= 0) return 0; diff --git a/src/r_mp3.h b/src/r_mp3.h index 42e9853bc..c55513cc3 100644 --- a/src/r_mp3.h +++ b/src/r_mp3.h @@ -33,7 +33,7 @@ class mp3_reader_c: public generic_reader_c { private: - unsigned char *chunk; + unsigned char chunk[16384]; mm_io_c *mm_io; class mp3_packetizer_c *mp3packetizer; int64_t bytes_processed, size;