diff --git a/ChangeLog b/ChangeLog index f1997f6ff..8581863dc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2003-05-22 Moritz Bunkus + * Support for reading AAC tracks from Matroska files. + * Released v0.4.0. 2003-05-21 Moritz Bunkus diff --git a/p_aac.cpp b/p_aac.cpp index e43f01e29..1c89d563e 100644 --- a/p_aac.cpp +++ b/p_aac.cpp @@ -13,7 +13,7 @@ /*! \file - \version \$Id: p_aac.cpp,v 1.5 2003/05/20 06:30:24 mosu Exp $ + \version \$Id: p_aac.cpp,v 1.6 2003/05/22 11:11:05 mosu Exp $ \brief AAC output module \author Moritz Bunkus */ @@ -33,7 +33,8 @@ using namespace LIBMATROSKA_NAMESPACE; aac_packetizer_c::aac_packetizer_c(generic_reader_c *nreader, int nid, int nprofile, unsigned long nsamples_per_sec, - int nchannels, track_info_t *nti) + int nchannels, track_info_t *nti, + bool nheaderless) throw (error_c): generic_packetizer_c(nreader, nti) { packetno = 0; bytes_output = 0; @@ -43,9 +44,10 @@ aac_packetizer_c::aac_packetizer_c(generic_reader_c *nreader, int nid, channels = nchannels; id = nid; profile = nprofile; + headerless = nheaderless; set_track_type(track_audio); - duplicate_data_on_add(false); + duplicate_data_on_add(headerless); } aac_packetizer_c::~aac_packetizer_c() { @@ -167,23 +169,23 @@ unsigned char *aac_packetizer_c::get_aac_packet(unsigned long *header, } void aac_packetizer_c::set_headers() { - if (id == 0) { - if (profile == 0) + if (id == AAC_ID_MPEG4) { + if (profile == AAC_PROFILE_MAIN) set_codec_id(MKV_A_AAC_4MAIN); - else if (profile == 1) + else if (profile == AAC_PROFILE_LC) set_codec_id(MKV_A_AAC_4LC); - else if (profile == 2) + else if (profile == AAC_PROFILE_SSR) set_codec_id(MKV_A_AAC_4SSR); - else if (profile == 3) + else if (profile == AAC_PROFILE_LTP) set_codec_id(MKV_A_AAC_4LTP); else die("aac_packetizer: Unknown AAC MPEG-4 object type..."); } else { - if (profile == 0) + if (profile == AAC_PROFILE_MAIN) set_codec_id(MKV_A_AAC_2MAIN); - else if (profile == 1) + else if (profile == AAC_PROFILE_LC) set_codec_id(MKV_A_AAC_2LC); - else if (profile == 2) + else if (profile == AAC_PROFILE_SSR) set_codec_id(MKV_A_AAC_2SSR); else die("aac_packetizer: Unknown AAC MPEG-2 profile..."); @@ -201,6 +203,18 @@ int aac_packetizer_c::process(unsigned char *buf, int size, aac_header_t aacheader; int64_t my_timecode; + if (headerless) { + if (timecode != -1) + my_timecode = timecode; + else + my_timecode = (int64_t)(1000.0 * packetno * 1024 * ti->async.linear / + samples_per_sec); + add_packet(buf, size, my_timecode, + (int64_t)(1000.0 * 1024 * ti->async.linear / samples_per_sec)); + + return EMOREDATA; + } + if (timecode != -1) my_timecode = timecode; diff --git a/p_aac.h b/p_aac.h index 960da4460..2cc22b2c4 100644 --- a/p_aac.h +++ b/p_aac.h @@ -13,7 +13,7 @@ /*! \file - \version \$Id: p_aac.h,v 1.5 2003/05/20 06:30:24 mosu Exp $ + \version \$Id: p_aac.h,v 1.6 2003/05/22 11:11:05 mosu Exp $ \brief class definition for the AAC output module \author Moritz Bunkus */ @@ -25,17 +25,27 @@ #include "pr_generic.h" #include "aac_common.h" +#define AAC_ID_MPEG4 0 +#define AAC_ID_MPEG2 1 + +#define AAC_PROFILE_MAIN 0 +#define AAC_PROFILE_LC 1 +#define AAC_PROFILE_SSR 2 +#define AAC_PROFILE_LTP 3 + class aac_packetizer_c: public generic_packetizer_c { private: int64_t bytes_output, packetno; unsigned long samples_per_sec; int channels, buffer_size, id, profile; unsigned char *packet_buffer; + bool headerless; public: aac_packetizer_c(generic_reader_c *nreader, int nid, int nprofile, unsigned long nsamples_per_sec, int nchannels, - track_info_t *nti) throw (error_c); + track_info_t *nti, bool nheaderless = false) + throw (error_c); virtual ~aac_packetizer_c(); virtual int process(unsigned char *buf, int size, int64_t timecode = -1, diff --git a/r_matroska.cpp b/r_matroska.cpp index 8808ce39f..bb2e4c1ac 100644 --- a/r_matroska.cpp +++ b/r_matroska.cpp @@ -13,7 +13,7 @@ /*! \file - \version \$Id: r_matroska.cpp,v 1.36 2003/05/21 22:17:33 mosu Exp $ + \version \$Id: r_matroska.cpp,v 1.37 2003/05/22 11:11:05 mosu Exp $ \brief Matroska reader \author Moritz Bunkus */ @@ -41,6 +41,7 @@ extern "C" { // for BITMAPINFOHEADER #include "p_mp3.h" #include "p_ac3.h" #include "p_dts.h" +#include "p_aac.h" #include "EbmlContexts.h" #include "EbmlHead.h" @@ -100,9 +101,6 @@ mkv_reader_c::mkv_reader_c(track_info_t *nti) throw (error_c): segment_duration = 0.0; - fprintf(stdout, "WARNING! Matroska files cannot be processed at the " - "moment.\n"); - if (!read_headers()) throw error_c("matroska_reader: Failed to read the headers."); @@ -197,7 +195,8 @@ void mkv_reader_c::verify_tracks() { if (!strcmp(t->codec_id, MKV_V_MSCOMP)) { if ((t->private_data == NULL) || (t->private_size < sizeof(BITMAPINFOHEADER))) { - printf("[mkv] WARNING: CodecID for track %u is '" MKV_V_MSCOMP + printf("matroska_reader: WARNING: CodecID for track %u is '" + MKV_V_MSCOMP "', but there was no BITMAPINFOHEADER struct present. " "Therefore we don't have a FourCC to identify the video " "codec used.\n", t->tnum); @@ -209,8 +208,8 @@ void mkv_reader_c::verify_tracks() { u = get_uint32(&bih->bi_width); if (t->v_width != u) { - printf("[mkv] WARNING: (MS compatibility mode, track %u) " - "Matrosa says video width is %u, but the " + printf("matroska_reader: WARNING: (MS compatibility mode, " + "track %u) Matrosa says video width is %u, but the " "BITMAPINFOHEADER says %u.\n", t->tnum, t->v_width, u); if (t->v_width == 0) t->v_width = u; @@ -218,9 +217,9 @@ void mkv_reader_c::verify_tracks() { u = get_uint32(&bih->bi_height); if (t->v_height != u) { - printf("[mkv] WARNING: (MS compatibility mode, track %u) " - "Matrosa video height is %u, but the BITMAPINFOHEADER " - "says %u.\n", t->tnum, t->v_height, u); + printf("matroska_reader: WARNING: (MS compatibility mode, " + "track %u) Matrosa video height is %u, but the " + "BITMAPINFOHEADER says %u.\n", t->tnum, t->v_height, u); if (t->v_height == 0) t->v_height = u; } @@ -228,23 +227,25 @@ void mkv_reader_c::verify_tracks() { memcpy(t->v_fourcc, &bih->bi_compression, 4); if (t->v_frate == 0.0) { - printf("[mkv] ERROR: (MS compatibility mode, track %u) " - "No VideoFrameRate element was found.\n", t->tnum); + printf("matroska_reader: ERROR: (MS compatibility mode, track " + "%u) No VideoFrameRate element was found.\n", t->tnum); continue; } } } else { - printf("[mkv] Native CodecIDs for video tracks are not supported " - "yet (track %u).\n", t->tnum); + printf("matroska_reader: Native CodecIDs for video tracks are not " + "supported yet (track %u).\n", t->tnum); continue; } if (t->v_width == 0) { - printf("[mkv] The width for track %u was not set.\n", t->tnum); + printf("matroska_reader: The width for track %u was not set.\n", + t->tnum); continue; } if (t->v_height == 0) { - printf("[mkv] The height for track %u was not set.\n", t->tnum); + printf("matroska_reader: The height for track %u was not set.\n", + t->tnum); continue; } @@ -259,10 +260,10 @@ void mkv_reader_c::verify_tracks() { if (!strcmp(t->codec_id, MKV_A_ACM)) { if ((t->private_data == NULL) || (t->private_size < sizeof(WAVEFORMATEX))) { - printf("[mkv] WARNING: CodecID for track %u is '" MKV_A_ACM "', " - "but there was no WAVEFORMATEX struct present. " - "Therefore we don't have a format ID to identify the audio " - "codec used.\n", t->tnum); + printf("matroska_reader: WARNING: CodecID for track %u is '" + MKV_A_ACM "', but there was no WAVEFORMATEX struct present." + " Therefore we don't have a format ID to identify the audio" + " codec used.\n", t->tnum); continue; } else { t->ms_compat = 1; @@ -270,19 +271,19 @@ void mkv_reader_c::verify_tracks() { wfe = (WAVEFORMATEX *)t->private_data; u = get_uint32(&wfe->n_samples_per_sec); if (((uint32_t)t->a_sfreq) != u) { - printf("[mkv] WARNING: (MS compatibility mode for track %u) " - "Matroska says that there are %u samples per second, " - "but WAVEFORMATEX says that there are %u.\n", t->tnum, - (uint32_t)t->a_sfreq, u); + printf("matroska_reader: WARNING: (MS compatibility mode for " + "track %u) Matroska says that there are %u samples per " + "second, but WAVEFORMATEX says that there are %u.\n", + t->tnum, (uint32_t)t->a_sfreq, u); if (t->a_sfreq == 0.0) t->a_sfreq = (float)u; } u = get_uint16(&wfe->n_channels); if (t->a_channels != u) { - printf("[mkv] WARNING: (MS compatibility mode for track %u) " - "Matroska says that there are %u channels, but the " - "WAVEFORMATEX says that there are %u.\n", t->tnum, + printf("matroska_reader: WARNING: (MS compatibility mode for " + "track %u) Matroska says that there are %u channels, but " + "the WAVEFORMATEX says that there are %u.\n", t->tnum, t->a_channels, u); if (t->a_channels == 0) t->a_channels = u; @@ -290,10 +291,10 @@ void mkv_reader_c::verify_tracks() { u = get_uint16(&wfe->w_bits_per_sample); if (t->a_bps != u) { - printf("[mkv] WARNING: (MS compatibility mode for track %u) " - "Matroska says that there are %u bits per sample, " - "but the WAVEFORMATEX says that there are %u.\n", t->tnum, - t->a_bps, u); + printf("matroska_reader: WARNING: (MS compatibility mode for " + "track %u) Matroska says that there are %u bits per " + "sample, but the WAVEFORMATEX says that there are %u.\n", + t->tnum, t->a_bps, u); if (t->a_bps == 0) t->a_bps = u; } @@ -311,14 +312,16 @@ void mkv_reader_c::verify_tracks() { t->a_formattag = 0x0001; else if (!strcmp(t->codec_id, MKV_A_VORBIS)) { if (t->private_data == NULL) { - printf("[mkv] WARNING: CodecID for track %u is 'A_VORBIS', " - "but there are no header packets present.", t->tnum); + printf("matroska_reader: WARNING: CodecID for track %u is " + "'A_VORBIS', but there are no header packets present.", + t->tnum); continue; } c = (unsigned char *)t->private_data; if (c[0] != 2) { - printf("[mkv] Vorbis track does not contain valid headers.\n"); + printf("matroska_reader: Vorbis track does not contain valid " + "headers.\n"); continue; } @@ -331,7 +334,8 @@ void mkv_reader_c::verify_tracks() { offset++; } if (offset >= (t->private_size - 1)) { - printf("[mkv] Vorbis track does not contain valid headers.\n"); + printf("matroska_reader: Vorbis track does not contain valid " + "headers.\n"); continue; } length += c[offset]; @@ -347,28 +351,37 @@ void mkv_reader_c::verify_tracks() { t->header_sizes[0] - t->header_sizes[1]; t->a_formattag = 0xFFFE; - } else { - printf("[mkv] Unknown/unsupported audio codec ID '%s' for track " - "%u.\n", t->codec_id, t->tnum); + } else if (!strcmp(t->codec_id, MKV_A_AAC_2MAIN) || + !strcmp(t->codec_id, MKV_A_AAC_2LC) || + !strcmp(t->codec_id, MKV_A_AAC_2SSR) || + !strcmp(t->codec_id, MKV_A_AAC_4MAIN) || + !strcmp(t->codec_id, MKV_A_AAC_4LC) || + !strcmp(t->codec_id, MKV_A_AAC_4SSR) || + !strcmp(t->codec_id, MKV_A_AAC_4LTP) || + !strcmp(t->codec_id, MKV_A_AAC_4SBR)) + t->a_formattag = FOURCC('M', 'P', '4', 'A'); + else { + printf("matroska_reader: Unknown/unsupported audio codec ID '%s' " + "for track %u.\n", t->codec_id, t->tnum); continue; } } if (t->a_sfreq == 0.0) { - printf("[mkv] The sampling frequency was not set for track %u.\n", - t->tnum); + printf("matroska_reader: The sampling frequency was not set for " + "track %u.\n", t->tnum); continue; } if (t->a_channels == 0) { - printf("[mkv] The number of channels was not set for track %u.\n", - t->tnum); + printf("matroska_reader: The number of channels was not set for " + "track %u.\n", t->tnum); continue; } if (t->a_formattag == 0) { - printf("[mkv] The audio format tag was not set for track %u.\n", - t->tnum); + printf("matroska_reader: The audio format tag was not set for track " + "%u.\n", t->tnum); continue; } @@ -382,13 +395,13 @@ void mkv_reader_c::verify_tracks() { break; default: // unknown track type!? error in demuxer... - printf("[mkv] Error: matroska_reader: unknown demuxer type for track " - "%u: '%c'\n", t->tnum, t->type); + printf("matroska_reader: Error: matroska_reader: unknown demuxer " + "type for track %u: '%c'\n", t->tnum, t->type); continue; } if (t->ok) - printf("[mkv] Track %u seems to be ok.\n", t->tnum); + printf("matroska_reader: Track %u seems to be ok.\n", t->tnum); } } @@ -470,7 +483,9 @@ int mkv_reader_c::read_headers() { fprintf(stdout, "matroska_reader: | + duration: %.3fs\n", segment_duration); - } else if (!is_ebmlvoid(l2)) + } else if (!is_ebmlvoid(l2) && + !(EbmlId(*l2) == KaxWritingApp::ClassInfos.GlobalId) && + !(EbmlId(*l2) == KaxMuxingApp::ClassInfos.GlobalId)) fprintf(stdout, "matroska_reader: | + unknown element@2: %s\n", typeid(*l2).name()); @@ -864,8 +879,7 @@ void mkv_reader_c::create_packetizers() { (unsigned long)t->a_sfreq, &nti); */ - } - else if (t->a_formattag == 0xFFFE) + } else if (t->a_formattag == 0xFFFE) t->packetizer = new vorbis_packetizer_c(this, t->headers[0], t->header_sizes[0], @@ -873,7 +887,38 @@ void mkv_reader_c::create_packetizers() { t->header_sizes[1], t->headers[2], t->header_sizes[2], &nti); - else { + else if (t->a_formattag == FOURCC('M', 'P', '4', 'A')) { + // A_AAC/MPEG2/MAIN + // 0123456789012345 + int id, profile; + + if (t->codec_id[10] == '2') + id = AAC_ID_MPEG2; + else if (t->codec_id[10] == '4') + id = AAC_ID_MPEG4; + else { + fprintf(stderr, "Error: matroska_reader: Malformed codec id " + "%s for track %d.\n", t->codec_id, t->tnum); + exit(1); + } + if (!strcmp(&t->codec_id[12], "MAIN")) + profile = AAC_PROFILE_MAIN; + else if (!strcmp(&t->codec_id[12], "LC")) + profile = AAC_PROFILE_LC; + else if (!strcmp(&t->codec_id[12], "SSR")) + profile = AAC_PROFILE_SSR; + else if (!strcmp(&t->codec_id[12], "LTP")) + profile = AAC_PROFILE_LTP; + else { + fprintf(stderr, "Error: matroska_reader: Malformed codec id " + "%s for track %d.\n", t->codec_id, t->tnum); + exit(1); + } + + t->packetizer = new aac_packetizer_c(this, id, profile, + (unsigned long)t->a_sfreq, + t->a_channels, &nti, true); + } else { fprintf(stderr, "Error: matroska_reader: Unsupported track type " "for track %d.\n", t->tnum); exit(1); @@ -995,7 +1040,8 @@ int mkv_reader_c::read() { } else if (!(EbmlId(*l3) == KaxBlockVirtual::ClassInfos.GlobalId)) - printf("[mkv] Uknown element@3: %s\n", typeid(*l3).name()); + printf("matroska_reader: Uknown element@3: %s\n", + typeid(*l3).name()); l3->SkipData(*es, l3->Generic().Context); if (delete_element) @@ -1055,7 +1101,8 @@ int mkv_reader_c::read() { } // while (l2 != NULL) } else if (!(EbmlId(*l1) == KaxCues::ClassInfos.GlobalId) && !is_ebmlvoid(l1)) - printf("[mkv] Unknown element@1: %s\n", typeid(*l1).name()); + printf("matroska_reader: Unknown element@1: %s\n", + typeid(*l1).name()); if (exit_loop) break; @@ -1074,7 +1121,7 @@ int mkv_reader_c::read() { } } // while (l1 != NULL) } catch (exception ex) { - printf("[mkv] exception caught\n"); + printf("matroska_reader: exception caught\n"); return 0; } diff --git a/r_matroska.h b/r_matroska.h index 96eb864ec..5098ddc2c 100644 --- a/r_matroska.h +++ b/r_matroska.h @@ -13,7 +13,7 @@ /*! \file - \version \$Id: r_matroska.h,v 1.13 2003/05/20 06:30:24 mosu Exp $ + \version \$Id: r_matroska.h,v 1.14 2003/05/22 11:11:05 mosu Exp $ \brief class definitions for the Matroska reader \author Moritz Bunkus */ @@ -50,9 +50,8 @@ typedef struct { char v_fourcc[5]; // Parameters for audio tracks - uint32_t a_channels, a_bps; + uint32_t a_channels, a_bps, a_formattag; float a_sfreq; - uint16_t a_formattag; void *private_data; unsigned int private_size;