From 1496394c0f4be1a718fb189846c6350cbd9e43da Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Thu, 24 Oct 2002 22:37:51 +0000 Subject: First stab at porting mplex Original commit message from CVS: First stab at porting mplex --- ext/mplex/ac3strm_in.cc | 382 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 ext/mplex/ac3strm_in.cc (limited to 'ext/mplex/ac3strm_in.cc') diff --git a/ext/mplex/ac3strm_in.cc b/ext/mplex/ac3strm_in.cc new file mode 100644 index 00000000..5dfc22d1 --- /dev/null +++ b/ext/mplex/ac3strm_in.cc @@ -0,0 +1,382 @@ +/* + * ac3strm_in.c: AC3 Audio strem class members handling scanning and + * buffering raw input stream. + * + * Copyright (C) 2001 Andrew Stevens + * Copyright (C) 2000,2001 Brent Byeler for original header-structure + * parsing code. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "audiostrm.hh" +#include "outputstream.hh" + + + +#define AC3_SYNCWORD 0x0b77 +#define AC3_PACKET_SAMPLES 1536 + +/// table for the available AC3 bitrates +static const unsigned int ac3_bitrate_index[32] = +{ + 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, + 224, 256, 320, 384, 448, 512, 576, 640, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static const unsigned int ac3_frame_size[3][32] = +{ + {64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, + 448, 512, 640, 768, 896, 1024, 1152, 1280, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {69, 87, 104, 121, 139, 174, 208, 243, 278, 348, 417, + 487, 557, 696, 835, 975, 1114, 1253, 1393, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {96, 120, 144, 168, 192, 240, 288, 336, 384, 480, 576, + 672, 768, 960, 1152, 1344, 1536, 1728, 1920, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} +}; + +/// table for the available AC3 frequencies +static const unsigned int ac3_frequency[4] = { 48000, 44100, 32000, 0 }; + + +AC3Stream::AC3Stream (IBitStream & ibs, OutputStream & into): +AudioStream (ibs, into) +{ +} + +bool +AC3Stream::Probe (IBitStream & bs) +{ + return bs.getbits (16) == AC3_SYNCWORD; +} + + +/************************************************************************* + * + * Reads initial stream parameters and displays feedback banner to users + * @param stream_num AC3 substream ID + *************************************************************************/ + + +void +AC3Stream::Init (const int stream_num) +{ + unsigned int framesize_code; + + this->stream_num = stream_num; + + MuxStream::Init (PRIVATE_STR_1, 1, // Buffer scale + default_buffer_size, + muxinto.vcd_zero_stuffing, + muxinto.buffers_in_audio, muxinto.always_buffers_in_audio); + mjpeg_info ("Scanning for header info: AC3 Audio stream %02x", stream_num); + + InitAUbuffer (); + AU_start = bs.bitcount (); + if (bs.getbits (16) == AC3_SYNCWORD) { + num_syncword++; + bs.getbits (16); // CRC field + frequency = bs.getbits (2); // Sample rate code + framesize_code = bs.getbits (6); // Frame size code + framesize = ac3_frame_size[frequency][framesize_code >> 1]; + framesize = (framesize_code & 1) && frequency == 1 ? (framesize + 1) << 1 : (framesize << 1); + + + size_frames[0] = framesize; + size_frames[1] = framesize; + num_frames[0]++; + access_unit.start = AU_start; + access_unit.length = framesize; + bit_rate = ac3_bitrate_index[framesize_code >> 1]; + samples_per_second = ac3_frequency[frequency]; + + + /* Presentation time-stamping */ + access_unit.PTS = static_cast < clockticks > (decoding_order) * + static_cast < clockticks > (AC3_PACKET_SAMPLES) * + static_cast < clockticks > (CLOCKS) / samples_per_second; + access_unit.DTS = access_unit.PTS; + access_unit.dorder = decoding_order; + ++decoding_order; + aunits.append (access_unit); + + } else { + mjpeg_error ("Invalid AC3 Audio stream header."); + exit (1); + } + + OutputHdrInfo (); +} + +/// @returns the current bitrate +unsigned int +AC3Stream::NominalBitRate () +{ + return bit_rate; +} + +/// Prefills the internal buffer for output multiplexing. +/// @param frames_to_buffer the number of audio frames to read ahead +void +AC3Stream::FillAUbuffer (unsigned int frames_to_buffer) +{ + unsigned int framesize_code; + + last_buffered_AU += frames_to_buffer; + mjpeg_debug ("Scanning %d MPEG audio frames to frame %d", frames_to_buffer, last_buffered_AU); + + static int header_skip = 5; // Initially skipped past 5 bytes of header + int skip; + bool bad_last_frame = false; + + while (!bs.eos () && + decoding_order < last_buffered_AU) { + skip = access_unit.length - header_skip; + if (skip & 0x1) + bs.getbits (8); + if (skip & 0x2) + bs.getbits (16); + skip = skip >> 2; + + for (int i = 0; i < skip; i++) { + bs.getbits (32); + } + + prev_offset = AU_start; + AU_start = bs.bitcount (); + if (AU_start - prev_offset != access_unit.length * 8) { + bad_last_frame = true; + break; + } + + /* Check we have reached the end of have another catenated + stream to process before finishing ... */ + if ((syncword = bs.getbits (16)) != AC3_SYNCWORD) { + if (!bs.eos ()) { + mjpeg_error_exit1 ("Can't find next AC3 frame - broken bit-stream?"); + } + break; + } + + bs.getbits (16); // CRC field + bs.getbits (2); // Sample rate code TOOD: check for change! + framesize_code = bs.getbits (6); + framesize = ac3_frame_size[frequency][framesize_code >> 1]; + framesize = (framesize_code & 1) && frequency == 1 ? (framesize + 1) << 1 : (framesize << 1); + + access_unit.start = AU_start; + access_unit.length = framesize; + access_unit.PTS = static_cast < clockticks > (decoding_order) * + static_cast < clockticks > (AC3_PACKET_SAMPLES) * + static_cast < clockticks > (CLOCKS) / samples_per_second;; + access_unit.DTS = access_unit.PTS; + access_unit.dorder = decoding_order; + decoding_order++; + aunits.append (access_unit); + num_frames[0]++; + + num_syncword++; + + +#ifdef DEBUG_AC3_HEADERS + /* Some stuff to generate frame-header information */ + printf ("bsid = %d\n", bs.getbits (5)); + printf ("bsmode = 0x%1x\n", bs.getbits (3)); + int acmode = bs.getbits (3); + + printf ("acmode = 0x%1x\n", acmode); + if ((acmode & 0x1) && (acmode != 1)) + printf ("cmixlev = %d\n", bs.getbits (2)); + if ((acmode & 0x4)) + printf ("smixlev = %d\n", bs.getbits (2)); + if (acmode == 2) + printf ("dsurr = %d\n", bs.getbits (2)); + printf ("lfeon = %d\n", bs.getbits (1)); + printf ("dialnorm = %02d\n", bs.getbits (5)); + int compre = bs.getbits (1); + + printf ("compre = %d\n", compre); + if (compre) + printf ("compr = %02d\n", bs.getbits (8)); + int langcode = bs.getbits (1); + + printf ("langcode = %d\n", langcode); + if (langcode) + printf ("langcod = 0x%02x\n", bs.getbits (8)); + + while (bs.bitcount () % 8 != 0) + bs.getbits (1); + header_skip = (bs.bitcount () - AU_start) / 8; +#endif + if (num_syncword >= old_frames + 10) { + mjpeg_debug ("Got %d frame headers.", num_syncword); + old_frames = num_syncword; + } + + + } + if (bad_last_frame) { + mjpeg_error_exit1 ("Last AC3 frame ended prematurely!\n"); + } + last_buffered_AU = decoding_order; + eoscan = bs.eos (); + +} + + +/// Closes the AC3 stream and prints some statistics. +void +AC3Stream::Close () +{ + stream_length = AU_start >> 3; + mjpeg_info ("AUDIO_STATISTICS: %02x", stream_id); + mjpeg_info ("Audio stream length %lld bytes.", stream_length); + mjpeg_info ("Syncwords : %8u", num_syncword); + mjpeg_info ("Frames : %8u padded", num_frames[0]); + mjpeg_info ("Frames : %8u unpadded", num_frames[1]); + + bs.close (); +} + +/************************************************************************* + OutputAudioInfo + gibt gesammelte Informationen zu den Audio Access Units aus. + + Prints information on audio access units +*************************************************************************/ + +void +AC3Stream::OutputHdrInfo () +{ + mjpeg_info ("AC3 AUDIO STREAM:"); + + mjpeg_info ("Bit rate : %8u bytes/sec (%3u kbit/sec)", bit_rate * 128, bit_rate); + + if (frequency == 3) + mjpeg_info ("Frequency : reserved"); + else + mjpeg_info ("Frequency : %d Hz", ac3_frequency[frequency]); + +} + +/** +Reads the bytes neccessary to complete the current packet payload. +@param to_read number of bytes to read +@param dst byte buffer pointer to read to +@returns the number of bytes read + */ +unsigned int +AC3Stream::ReadPacketPayload (uint8_t * dst, unsigned int to_read) +{ + static unsigned int aus = 0; + static unsigned int rd = 0; + + unsigned int bytes_read = bs.read_buffered_bytes (dst + 4, to_read - 4); + + rd += bytes_read; + clockticks decode_time; + + unsigned int first_header = (new_au_next_sec || au_unsent > bytes_read) + ? 0 : au_unsent; + + // BUG BUG BUG: how do we set the 1st header pointer if we have + // the *middle* part of a large frame? + assert (first_header <= to_read - 2); + + + unsigned int syncwords = 0; + unsigned int bytes_muxed = bytes_read; + + if (bytes_muxed == 0 || MuxCompleted ()) { + goto completion; + } + + + /* Work through what's left of the current AU and the following AU's + updating the info until we reach a point where an AU had to be + split between packets. + NOTE: It *is* possible for this loop to iterate. + + The DTS/PTS field for the packet in this case would have been + given the that for the first AU to start in the packet. + + */ + + decode_time = RequiredDTS (); + while (au_unsent < bytes_muxed) { + // BUG BUG BUG: if we ever had odd payload / packet size we might + // split an AC3 frame in the middle of the syncword! + assert (bytes_muxed > 1); + bufmodel.Queued (au_unsent, decode_time); + bytes_muxed -= au_unsent; + if (new_au_next_sec) + ++syncwords; + aus += au->length; + if (!NextAU ()) { + goto completion; + } + new_au_next_sec = true; + decode_time = RequiredDTS (); + }; + + // We've now reached a point where the current AU overran or + // fitted exactly. We need to distinguish the latter case + // so we can record whether the next packet starts with an + // existing AU or not - info we need to decide what PTS/DTS + // info to write at the start of the next packet. + + if (au_unsent > bytes_muxed) { + if (new_au_next_sec) + ++syncwords; + bufmodel.Queued (bytes_muxed, decode_time); + au_unsent -= bytes_muxed; + new_au_next_sec = false; + } else // if (au_unsent == bytes_muxed) + { + bufmodel.Queued (bytes_muxed, decode_time); + if (new_au_next_sec) + ++syncwords; + aus += au->length; + new_au_next_sec = NextAU (); + } +completion: + // Generate the AC3 header... + // Note the index counts from the low byte of the offset so + // the smallest value is 1! + + dst[0] = AC3_SUB_STR_0 + stream_num; + dst[1] = syncwords; + dst[2] = (first_header + 1) >> 8; + dst[3] = (first_header + 1) & 0xff; + + return bytes_read + 4; +} + + + +/* + * Local variables: + * c-file-style: "stroustrup" + * tab-width: 4 + * indent-tabs-mode: nil + * End: + */ -- cgit v1.2.1