Rewrote the complete MP3 handling. ID3 tags and all MPEG audio files should be handled correctly now.

This commit is contained in:
Moritz Bunkus 2003-09-24 16:40:20 +00:00
parent 0e566da8b9
commit c4146cc00a
8 changed files with 112 additions and 123 deletions

View File

@ -1,3 +1,10 @@
2003-09-24 Moritz Bunkus <moritz@bunkus.org>
* 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 <moritz@bunkus.org>
* mkvextract: new feature: Support for extract HE-AAC tracks to

View File

@ -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();
}

View File

@ -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|"

View File

@ -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];

View File

@ -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));
}

View File

@ -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();

View File

@ -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;

View File

@ -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;