diff --git a/ChangeLog b/ChangeLog index f06e7fcb2..4992a48cd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ 2003-08-14 Moritz Bunkus + * mkvmerge: Low bitrate AC3 tracks from Real's DNET are identified + as A_AC3/BSID9 or A_AC3/BSID10. + * mkvmerge: The RealMedia reader takes the number of packets into account when reading which results in better end-of-file detection. diff --git a/src/ac3_common.cpp b/src/ac3_common.cpp index d6ac89876..ec041ccee 100644 --- a/src/ac3_common.cpp +++ b/src/ac3_common.cpp @@ -22,6 +22,21 @@ #include "ac3_common.h" +/* + AC3 Header: + AAAAAAAA AAAAAAAA BBBBBBBB BBBBBBBB CCDDDDDD EEEEEFFF + A = sync, always 0x0B77 + B = CRC 16 of 5/8 frame + C = samplerate: + if E <= 8: 00 = 48kHz; 01 = 44,1kHz; 10 = 32kHz; 11 = reserved + if E = 9: 00 = 24kHz; 01 = 22,05kHz; 10 = 16kHz; 11 = reserved + if E = 10: 00 = 12kHz; 01 = 11,025kHz; 10 = 8KHz; 11 = reserved + D = framesize code, 12/24KHz is like 48kHz, 8/16kHz like 32kHz etc. + E = bitstream ID, if <=8 compatible to all standard decoders + 9 and 10 = low samplerate additions + F = bitstream mode +*/ + int find_ac3_header(unsigned char *buf, int size, ac3_header_t *ac3_header) { static int rate[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 576, 640}; @@ -90,6 +105,7 @@ int find_ac3_header(unsigned char *buf, int size, ac3_header_t *ac3_header) { } if (header.flags & A52_LFE) header.channels++; + header.bsid = (buf[i + 5] >> 3); memcpy(ac3_header, &header, sizeof(ac3_header_t)); return i; diff --git a/src/ac3_common.h b/src/ac3_common.h index f8b952d22..b59bd75d3 100644 --- a/src/ac3_common.h +++ b/src/ac3_common.h @@ -42,6 +42,7 @@ typedef struct { int channels; int flags; int bytes; + int bsid; } ac3_header_t; int find_ac3_header(unsigned char *buf, int size, ac3_header_t *ac3_header); diff --git a/src/p_ac3.cpp b/src/p_ac3.cpp index db39649b8..dbe8b6ae5 100644 --- a/src/p_ac3.cpp +++ b/src/p_ac3.cpp @@ -32,7 +32,7 @@ using namespace libmatroska; ac3_packetizer_c::ac3_packetizer_c(generic_reader_c *nreader, unsigned long nsamples_per_sec, - int nchannels, track_info_t *nti) + int nchannels, int nbsid, track_info_t *nti) throw (error_c): generic_packetizer_c(nreader, nti) { packetno = 0; bytes_output = 0; @@ -40,6 +40,7 @@ ac3_packetizer_c::ac3_packetizer_c(generic_reader_c *nreader, buffer_size = 0; samples_per_sec = nsamples_per_sec; channels = nchannels; + bsid = nbsid; set_track_type(track_audio); set_track_default_duration_ns((int64_t)(1536000000000.0 *ti->async.linear / @@ -148,7 +149,13 @@ unsigned char *ac3_packetizer_c::get_ac3_packet(unsigned long *header, } void ac3_packetizer_c::set_headers() { - set_codec_id(MKV_A_AC3); + string id = MKV_A_AC3; + + if (bsid == 9) + id += "/BSID9"; + else if (bsid == 10) + id += "/BSID10"; + set_codec_id(id.c_str()); set_audio_sampling_freq((float)samples_per_sec); set_audio_channels(channels); @@ -190,9 +197,10 @@ void ac3_packetizer_c::dump_debug_info() { ac3_bs_packetizer_c::ac3_bs_packetizer_c(generic_reader_c *nreader, unsigned long nsamples_per_sec, - int nchannels, track_info_t *nti) + int nchannels, int nbsid, + track_info_t *nti) throw (error_c): ac3_packetizer_c(nreader, nsamples_per_sec, nchannels, - nti) { + nbsid, nti) { bsb_present = false; } diff --git a/src/p_ac3.h b/src/p_ac3.h index 7d26bd335..f04933119 100644 --- a/src/p_ac3.h +++ b/src/p_ac3.h @@ -31,12 +31,13 @@ class ac3_packetizer_c: public generic_packetizer_c { protected: int64_t bytes_output, packetno; unsigned long samples_per_sec; - int channels, buffer_size; + int channels, buffer_size, bsid; unsigned char *packet_buffer; public: ac3_packetizer_c(generic_reader_c *nreader, unsigned long nsamples_per_sec, - int nchannels, track_info_t *nti) throw (error_c); + int nchannels, int nbsid, track_info_t *nti) + throw (error_c); virtual ~ac3_packetizer_c(); virtual int process(unsigned char *buf, int size, int64_t timecode = -1, @@ -62,7 +63,8 @@ protected: public: ac3_bs_packetizer_c(generic_reader_c *nreader, unsigned long nsamples_per_sec, - int nchannels, track_info_t *nti) throw (error_c); + int nchannels, int nbsid, track_info_t *nti) + throw (error_c); protected: virtual void add_to_buffer(unsigned char *buf, int size); diff --git a/src/pr_generic.cpp b/src/pr_generic.cpp index 79dc2d5fd..e51b4a541 100644 --- a/src/pr_generic.cpp +++ b/src/pr_generic.cpp @@ -232,7 +232,7 @@ int generic_packetizer_c::get_track_num() { return hserialno; } -void generic_packetizer_c::set_codec_id(char *id) { +void generic_packetizer_c::set_codec_id(const char *id) { safefree(hcodec_id); if (id == NULL) { hcodec_id = NULL; @@ -241,7 +241,8 @@ void generic_packetizer_c::set_codec_id(char *id) { hcodec_id = safestrdup(id); } -void generic_packetizer_c::set_codec_private(unsigned char *cp, int length) { +void generic_packetizer_c::set_codec_private(const unsigned char *cp, + int length) { safefree(hcodec_private); if (cp == NULL) { hcodec_private = NULL; @@ -330,7 +331,7 @@ void generic_packetizer_c::force_default_track(int type) { default_tracks[idx] = hserialno; } -void generic_packetizer_c::set_language(char *language) { +void generic_packetizer_c::set_language(const char *language) { safefree(ti->language); ti->language = safestrdup(language); } diff --git a/src/pr_generic.h b/src/pr_generic.h index 51560ee5e..531b2c79f 100644 --- a/src/pr_generic.h +++ b/src/pr_generic.h @@ -177,10 +177,10 @@ public: virtual int set_uid(uint32_t uid); virtual void set_track_type(int type); virtual int get_track_type(); - virtual void set_language(char *language); + virtual void set_language(const char *language); - virtual void set_codec_id(char *id); - virtual void set_codec_private(unsigned char *cp, int length); + virtual void set_codec_id(const char *id); + virtual void set_codec_private(const unsigned char *cp, int length); virtual void set_track_min_cache(int min_cache); virtual void set_track_max_cache(int max_cache); diff --git a/src/r_ac3.cpp b/src/r_ac3.cpp index 8a651ec2e..d1c6b06a0 100644 --- a/src/r_ac3.cpp +++ b/src/r_ac3.cpp @@ -80,7 +80,7 @@ ac3_reader_c::ac3_reader_c(track_info_t *nti) throw (error_c): bytes_processed = 0; ti->id = 0; // ID for this track. ac3packetizer = new ac3_packetizer_c(this, ac3header.sample_rate, - ac3header.channels, ti); + ac3header.channels, ac3header.bsid, ti); if (verbose) mxprint(stdout, "Using AC3 demultiplexer for %s.\n+-> Using " "AC3 output module for audio stream.\n", ti->fname); diff --git a/src/r_avi.cpp b/src/r_avi.cpp index 6733c9182..2901c27e9 100644 --- a/src/r_avi.cpp +++ b/src/r_avi.cpp @@ -229,7 +229,7 @@ void avi_reader_c::add_audio_demuxer(avi_t *avi, int aid) { demuxer->bits_per_sample = AVI_audio_mp3rate(avi); demuxer->packetizer = new ac3_packetizer_c(this, demuxer->samples_per_second, - demuxer->channels, ti); + demuxer->channels, 0, ti); break; default: mxprint(stderr, "Error: Unknown audio format 0x%04x for audio track ID " diff --git a/src/r_matroska.cpp b/src/r_matroska.cpp index d49deba29..a880b75f3 100644 --- a/src/r_matroska.cpp +++ b/src/r_matroska.cpp @@ -335,7 +335,7 @@ void kax_reader_c::verify_tracks() { } else { if (!strcmp(t->codec_id, MKV_A_MP3)) t->a_formattag = 0x0055; - else if (!strcmp(t->codec_id, MKV_A_AC3)) + else if (!strncmp(t->codec_id, MKV_A_AC3, strlen(MKV_A_AC3))) t->a_formattag = 0x2000; else if (!strcmp(t->codec_id, MKV_A_DTS)) t->a_formattag = 0x2001; @@ -1141,9 +1141,17 @@ void kax_reader_c::create_packetizers() { mxprint(stdout, "Matroska demultiplexer (%s): using the MP3 " "output module for track ID %u.\n", ti->fname, t->tnum); } else if (t->a_formattag == 0x2000) { + int bsid; + + if (!strcmp(t->codec_id, "A_AC3/BSID9")) + bsid = 9; + else if (!strcmp(t->codec_id, "A_AC3/BSID10")) + bsid = 10; + else + bsid = 0; t->packetizer = new ac3_packetizer_c(this, (unsigned long)t->a_sfreq, - t->a_channels, &nti); + t->a_channels, bsid, &nti); if (verbose) mxprint(stdout, "Matroska demultiplexer (%s): using the AC3 " "output module for track ID %u.\n", ti->fname, t->tnum); diff --git a/src/r_ogm.cpp b/src/r_ogm.cpp index 153a0f486..5ec47be37 100644 --- a/src/r_ogm.cpp +++ b/src/r_ogm.cpp @@ -291,7 +291,7 @@ void ogm_reader_c::create_packetizers() { try { dmx->packetizer = new ac3_packetizer_c(this, get_uint64(&sth->samples_per_unit), - get_uint16(&sth->sh.audio.channels), ti); + get_uint16(&sth->sh.audio.channels), 0, ti); } catch (error_c &error) { mxprint(stderr, "Error: ogm_reader: could not initialize AC3 " "packetizer for stream id %d. Will try to continue and " diff --git a/src/r_real.cpp b/src/r_real.cpp index 341239f24..700666981 100644 --- a/src/r_real.cpp +++ b/src/r_real.cpp @@ -174,6 +174,7 @@ real_reader_c::real_reader_c(track_info_t *nti) throw (error_c): mxprint(stdout, "Using RealMedia demultiplexer for %s.\n", ti->fname); parse_headers(); + get_information_from_data(); if (!identifying) create_packetizers(); } @@ -468,7 +469,7 @@ void real_reader_c::create_packetizers() { if (!strncmp(dmx->fourcc, "dnet", 4)) { dmx->packetizer = new ac3_bs_packetizer_c(this, dmx->samples_per_second, dmx->channels, - ti); + dmx->bsid, ti); if (verbose) mxprint(stdout, "+-> Using AC3 output module for stream " "%u (FourCC: %s).\n", dmx->id, dmx->fourcc); @@ -945,3 +946,68 @@ void real_reader_c::set_dimensions(real_demuxer_t *dmx, unsigned char *buffer, } // }}} + +void real_reader_c::get_information_from_data() { + uint32_t length, id; + int i; + unsigned char *chunk; + real_demuxer_t *dmx; + bool done; + + io->save_pos(); + + done = true; + for (i = 0; i < demuxers.size(); i++) { + dmx = demuxers[i]; + if (!strcasecmp(dmx->fourcc, "DNET")) { + dmx->bsid = -1; + done = false; + } + } + + try { + while (!done) { + io->skip(2); + length = io->read_uint16_be(); + id = io->read_uint16_be(); + io->skip(4 + 1 + 1); + + if (length < 12) + die("real_reader: Data packet too small."); + + dmx = find_demuxer(id); + + if (dmx == NULL) { + io->skip(length - 12); + continue; + } + + length -= 12; + + chunk = (unsigned char *)safemalloc(length); + if (io->read(chunk, length) != length) + break; + + num_packets++; + + if (!strcasecmp(dmx->fourcc, "DNET")) + dmx->bsid = chunk[4] >> 3; + + safefree(chunk); + + done = true; + for (i = 0; i < demuxers.size(); i++) { + dmx = demuxers[i]; + if (!strcasecmp(dmx->fourcc, "DNET") && (dmx->bsid == -1)) + done = false; + + } + } + + io->restore_pos(); + num_packets = 0; + + } catch (exception &ex) { + } +} + diff --git a/src/r_real.h b/src/r_real.h index dd300c7b2..85dc99b0a 100644 --- a/src/r_real.h +++ b/src/r_real.h @@ -44,7 +44,7 @@ typedef struct { uint32_t start_time, preroll; - int channels, bits_per_sample, samples_per_second; + int channels, bits_per_sample, samples_per_second, bsid; int width, height; float fps; @@ -97,6 +97,7 @@ protected: uint32_t &height); virtual void set_dimensions(real_demuxer_t *dmx, unsigned char *buffer, int size); + virtual void get_information_from_data(); }; #endif // __R_REAL_H