/* mkvmerge -- utility for splicing together matroska files from component media subtypes p_aac.h Written by Moritz Bunkus Distributed under the GPL see the file COPYING for details or visit http://www.gnu.org/copyleft/gpl.html */ /*! \file \version \$Id: p_aac.cpp,v 1.8 2003/05/26 21:49:11 mosu Exp $ \brief AAC output module \author Moritz Bunkus */ #include #include #include #include #include "pr_generic.h" #include "aac_common.h" #include "p_aac.h" #include "matroska.h" 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, bool nheaderless) throw (error_c): generic_packetizer_c(nreader, nti) { packetno = 0; bytes_output = 0; packet_buffer = NULL; buffer_size = 0; samples_per_sec = nsamples_per_sec; channels = nchannels; id = nid; profile = nprofile; headerless = nheaderless; set_track_type(track_audio); duplicate_data_on_add(headerless); } aac_packetizer_c::~aac_packetizer_c() { if (packet_buffer != NULL) safefree(packet_buffer); } void aac_packetizer_c::add_to_buffer(unsigned char *buf, int size) { unsigned char *new_buffer; new_buffer = (unsigned char *)saferealloc(packet_buffer, buffer_size + size); memcpy(new_buffer + buffer_size, buf, size); packet_buffer = new_buffer; buffer_size += size; } int aac_packetizer_c::aac_packet_available() { int pos; aac_header_t aacheader; if (packet_buffer == NULL) return 0; pos = find_aac_header(packet_buffer, buffer_size, &aacheader); if (pos < 0) return 0; return 1; } void aac_packetizer_c::remove_aac_packet(int pos, int framesize) { int new_size; unsigned char *temp_buf; new_size = buffer_size - (pos + framesize); if (new_size != 0) temp_buf = (unsigned char *)safememdup(&packet_buffer[pos + framesize], new_size); else temp_buf = NULL; safefree(packet_buffer); packet_buffer = temp_buf; buffer_size = new_size; } unsigned char *aac_packetizer_c::get_aac_packet(unsigned long *header, aac_header_t *aacheader) { int pos, i, up_shift, down_shift; unsigned char *buf, *src; double pims; if (packet_buffer == NULL) return 0; pos = find_aac_header(packet_buffer, buffer_size, aacheader); if (pos < 0) return 0; if ((pos + aacheader->bytes) > buffer_size) return 0; pims = ((double)aacheader->bytes) * 1000.0 / ((double)aacheader->bit_rate / 8.0); if (ti->async.displacement < 0) { /* * AAC audio synchronization. displacement < 0 means skipping an * appropriate number of packets at the beginning. */ ti->async.displacement += (int)pims; if (ti->async.displacement > -(pims / 2)) ti->async.displacement = 0; remove_aac_packet(pos, aacheader->bytes); return 0; } if (verbose && (pos > 0)) fprintf(stdout, "aac_packetizer: skipping %d bytes (no valid AAC header " "found). This might make audio/video go out of sync, but this " "stream is damaged.\n", pos); if ((aacheader->header_bit_size % 8) == 0) buf = (unsigned char *)safememdup(packet_buffer + pos + aacheader->header_byte_size, aacheader->data_byte_size); else { // Header is not byte aligned, i.e. MPEG-4 ADTS // This code is from mpeg4ip/server/mp4creator/aac.cpp up_shift = aacheader->header_bit_size % 8; down_shift = 8 - up_shift; src = packet_buffer + pos + aacheader->header_bit_size / 8; buf = (unsigned char *)safemalloc(aacheader->data_byte_size); buf[0] = src[0] << up_shift; for (i = 1; i < aacheader->data_byte_size; i++) { buf[i - 1] |= (src[i] >> down_shift); buf[i] = (src[i] << up_shift); } } if (ti->async.displacement > 0) { /* * AAC audio synchronization. displacement > 0 is solved by duplicating * the very first AAC packet as often as necessary. I cannot create * a packet with total silence because I don't know how, and simply * settings the packet's values to 0 does not work as the AAC header * contains a CRC of its data. */ ti->async.displacement -= (int)pims; if (ti->async.displacement < (pims / 2)) ti->async.displacement = 0; return buf; } remove_aac_packet(pos, aacheader->bytes); return buf; } void aac_packetizer_c::set_headers() { if (id == AAC_ID_MPEG4) { if (profile == AAC_PROFILE_MAIN) set_codec_id(MKV_A_AAC_4MAIN); else if (profile == AAC_PROFILE_LC) set_codec_id(MKV_A_AAC_4LC); else if (profile == AAC_PROFILE_SSR) set_codec_id(MKV_A_AAC_4SSR); else if (profile == AAC_PROFILE_LTP) set_codec_id(MKV_A_AAC_4LTP); else die("aac_packetizer: Unknown AAC MPEG-4 object type %d.", profile); } else { if (profile == AAC_PROFILE_MAIN) set_codec_id(MKV_A_AAC_2MAIN); else if (profile == AAC_PROFILE_LC) set_codec_id(MKV_A_AAC_2LC); else if (profile == AAC_PROFILE_SSR) set_codec_id(MKV_A_AAC_2SSR); else die("aac_packetizer: Unknown AAC MPEG-2 profile %d.", profile); } set_audio_sampling_freq((float)samples_per_sec); set_audio_channels(channels); generic_packetizer_c::set_headers(); } int aac_packetizer_c::process(unsigned char *buf, int size, int64_t timecode, int64_t, int64_t, int64_t) { unsigned char *packet; unsigned long header; aac_header_t aacheader; int64_t my_timecode; debug_enter("aac_packetizer_c::process"); 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)); debug_leave("aac_packetizer_c::process"); return EMOREDATA; } if (timecode != -1) my_timecode = timecode; add_to_buffer(buf, size); while ((packet = get_aac_packet(&header, &aacheader)) != NULL) { if (timecode == -1) my_timecode = (int64_t)(1000.0 * packetno * 1024 * ti->async.linear / samples_per_sec); add_packet(packet, aacheader.data_byte_size, my_timecode, (int64_t)(1000.0 * 1024 * ti->async.linear / samples_per_sec)); packetno++; } debug_leave("aac_packetizer_c::process"); return EMOREDATA; } void aac_packetizer_c::dump_debug_info() { fprintf(stderr, "DBG> aac_packetizer_c: queue: %d; buffer size: %d\n", packet_queue.size(), buffer_size); }