mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 11:54:01 +00:00
Rewrote the complete MP3 handling. ID3 tags and all MPEG audio files should be handled correctly now.
This commit is contained in:
parent
0e566da8b9
commit
c4146cc00a
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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|"
|
||||
|
@ -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];
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
117
src/r_mp3.cpp
117
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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user