| /* |
| * audiostrm_in.c: MPEG Audio strem class members handling scanning |
| * and buffering raw input stream. |
| * |
| * Copyright (C) 2001 Andrew Stevens <andrew.stevens@philips.com> |
| * |
| * |
| * 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 <config.h> |
| #include <math.h> |
| #include <stdlib.h> |
| |
| #include "audiostrm.hh" |
| #include "outputstream.hh" |
| |
| |
| static const char *mpa_audio_version[4] = { |
| "2.5", |
| "2.0", |
| "reserved", |
| "1.0" |
| }; |
| |
| static const unsigned int mpa_bitrates_kbps[4][3][16] = { |
| { /* MPEG audio V2.5 */ |
| {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0}, |
| {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}, |
| {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0} |
| }, |
| { /*RESERVED*/ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, |
| {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} |
| }, |
| { /* MPEG audio V2 */ |
| {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0}, |
| {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0}, |
| {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0} |
| }, |
| { /* MPEG audio V1 */ |
| {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0}, |
| {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0}, |
| {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0} |
| } |
| |
| }; |
| |
| |
| static const int mpa_freq_table[4][4] = { |
| /* MPEG audio V2.5 */ |
| {11025, 12000, 8000, 0}, |
| /* RESERVED */ |
| {0, 0, 0, 0}, |
| /* MPEG audio V2 */ |
| {22050, 24000, 16000, 0}, |
| /* MPEG audio V1 */ |
| {44100, 48000, 32000, 0} |
| }; |
| |
| static const char mpa_stereo_mode[4][15] = |
| { "stereo", "joint stereo", "dual channel", "single channel" }; |
| static const char mpa_copyright_status[2][20] = { "no copyright", "copyright protected" }; |
| static const char mpa_original_bit[2][10] = { "copy", "original" }; |
| static const char mpa_emphasis_mode[4][20] = |
| { "none", "50/15 microseconds", "reserved", "CCITT J.17" }; |
| static const unsigned int mpa_slots[4] = { 12, 144, 144, 0 }; |
| static const unsigned int mpa_samples[4] = { 384, 1152, 1152, 0 }; |
| |
| |
| |
| MPAStream::MPAStream (IBitStream & ibs, OutputStream & into): |
| AudioStream (ibs, into) |
| { |
| } |
| |
| bool |
| MPAStream::Probe (IBitStream & bs) |
| { |
| return bs.getbits (11) == AUDIO_SYNCWORD; |
| } |
| |
| |
| /************************************************************************* |
| * |
| * Reads initial stream parameters and displays feedback banner to users |
| * |
| *************************************************************************/ |
| |
| |
| void |
| MPAStream::Init (const int stream_num) |
| { |
| int padding_bit; |
| |
| MuxStream::Init (AUDIO_STR_0 + stream_num, 0, // Buffer scale |
| muxinto.audio_buffer_size, |
| muxinto.vcd_zero_stuffing, |
| muxinto.buffers_in_audio, muxinto.always_buffers_in_audio); |
| mjpeg_info ("Scanning for header info: Audio stream %02x", AUDIO_STR_0 + stream_num); |
| |
| InitAUbuffer (); |
| |
| /* A.Stevens 2000 - update to be compatible up to MPEG2.5 |
| */ |
| AU_start = bs.bitcount (); |
| if (bs.getbits (11) == AUDIO_SYNCWORD) { |
| num_syncword++; |
| version_id = bs.getbits (2); |
| layer = 3 - bs.getbits (2); /* 0..2 not 1..3!! */ |
| protection = bs.get1bit (); |
| bit_rate_code = bs.getbits (4); |
| frequency = bs.getbits (2); |
| padding_bit = bs.get1bit (); |
| bs.get1bit (); |
| mode = bs.getbits (2); |
| mode_extension = bs.getbits (2); |
| copyright = bs.get1bit (); |
| original_copy = bs.get1bit (); |
| emphasis = bs.getbits (2); |
| |
| framesize = |
| mpa_bitrates_kbps[version_id][layer][bit_rate_code] * |
| mpa_slots[layer] * 1000 / mpa_freq_table[version_id][frequency]; |
| |
| size_frames[0] = framesize; |
| size_frames[1] = framesize + (layer == 0 ? 4 : 1); |
| num_frames[padding_bit]++; |
| access_unit.start = AU_start; |
| access_unit.length = size_frames[padding_bit]; |
| |
| samples_per_second = mpa_freq_table[version_id][frequency]; |
| |
| /* Presentation time-stamping */ |
| access_unit.PTS = static_cast < clockticks > (decoding_order) * |
| static_cast < clockticks > (mpa_samples[layer]) * |
| 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 MPEG Audio stream header."); |
| exit (1); |
| } |
| |
| |
| OutputHdrInfo (); |
| } |
| |
| unsigned int |
| MPAStream::NominalBitRate () |
| { |
| return mpa_bitrates_kbps[version_id][layer][bit_rate_code] * 128; |
| } |
| |
| |
| unsigned int |
| MPAStream::SizeFrame (int rate_code, int padding) |
| { |
| return mpa_bitrates_kbps[version_id][layer][rate_code] * |
| mpa_slots[layer] * 1000 / mpa_freq_table[version_id][frequency] + padding; |
| } |
| |
| void |
| MPAStream::FillAUbuffer (unsigned int frames_to_buffer) |
| { |
| unsigned int i; |
| unsigned int padding_bit; |
| |
| last_buffered_AU += frames_to_buffer; |
| |
| mjpeg_debug ("Scanning %d MPEG audio frames to frame %d", frames_to_buffer, last_buffered_AU); |
| |
| while (!bs.eos () && |
| decoding_order < last_buffered_AU) { |
| |
| skip = access_unit.length - 4; |
| if (skip & 0x1) |
| bs.getbits (8); |
| if (skip & 0x2) |
| bs.getbits (16); |
| skip = skip >> 2; |
| |
| for (i = 0; i < skip; i++) { |
| bs.getbits (32); |
| } |
| prev_offset = AU_start; |
| AU_start = bs.bitcount (); |
| |
| /* Check we have reached the end of have another catenated |
| stream to process before finishing ... */ |
| if ((syncword = bs.getbits (11)) != AUDIO_SYNCWORD) { |
| if (!bs.eobs) { |
| /* There appears to be another catenated stream... */ |
| int next; |
| |
| mjpeg_warn ("End of component bit-stream ... seeking next"); |
| /* Catenated stream must start on byte boundary */ |
| syncword = (syncword << (8 - AU_start % 8)); |
| next = bs.getbits (8 - (AU_start % 8)); |
| AU_start = bs.bitcount () - 11; |
| syncword = syncword | next; |
| if (syncword != AUDIO_SYNCWORD) { |
| mjpeg_warn ("Failed to find start of next stream at %lld prev %lld !", AU_start / 8, |
| prev_offset / 8); |
| break; |
| } |
| } else |
| /* No catenated stream... finished! */ |
| break; |
| } |
| // Skip version_id:2, layer:2, protection:1 |
| (void) bs.getbits (5); |
| int rate_code = bs.getbits (4); |
| |
| // Skip frequency |
| (void) bs.getbits (2); |
| |
| padding_bit = bs.get1bit (); |
| access_unit.start = AU_start; |
| access_unit.length = SizeFrame (rate_code, padding_bit); |
| access_unit.PTS = |
| static_cast < clockticks > (decoding_order) * static_cast < clockticks > |
| (mpa_samples[layer]) * 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[padding_bit]++; |
| |
| bs.getbits (9); |
| |
| num_syncword++; |
| |
| if (num_syncword >= old_frames + 10) { |
| mjpeg_debug ("Got %d frame headers.", num_syncword); |
| old_frames = num_syncword; |
| |
| } |
| |
| |
| |
| } |
| last_buffered_AU = decoding_order; |
| eoscan = bs.eos (); |
| |
| } |
| |
| |
| |
| void |
| MPAStream::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 |
| MPAStream::OutputHdrInfo () |
| { |
| unsigned int bitrate; |
| |
| bitrate = mpa_bitrates_kbps[version_id][layer][bit_rate_code]; |
| |
| |
| mjpeg_info ("AUDIO STREAM:"); |
| mjpeg_info ("Audio version : %s", mpa_audio_version[version_id]); |
| mjpeg_info ("Layer : %8u", layer + 1); |
| |
| if (protection == 0) |
| mjpeg_info ("CRC checksums : yes"); |
| else |
| mjpeg_info ("CRC checksums : no"); |
| |
| if (bit_rate_code == 0) |
| mjpeg_info ("Bit rate : free"); |
| else if (bit_rate_code == 0xf) |
| mjpeg_info ("Bit rate : reserved"); |
| else |
| mjpeg_info ("Bit rate : %8u bytes/sec (%3u kbit/sec)", bitrate * 128, bitrate); |
| |
| if (frequency == 3) |
| mjpeg_info ("Frequency : reserved"); |
| else |
| mjpeg_info ("Frequency : %d Hz", mpa_freq_table[version_id][frequency]); |
| |
| mjpeg_info ("Mode : %8u %s", mode, mpa_stereo_mode[mode]); |
| mjpeg_info ("Mode extension : %8u", mode_extension); |
| mjpeg_info ("Copyright bit : %8u %s", copyright, mpa_copyright_status[copyright]); |
| mjpeg_info ("Original/Copy : %8u %s", original_copy, mpa_original_bit[original_copy]); |
| mjpeg_info ("Emphasis : %8u %s", emphasis, mpa_emphasis_mode[emphasis]); |
| } |
| |
| |
| |
| /* |
| * Local variables: |
| * c-file-style: "stroustrup" |
| * tab-width: 4 |
| * indent-tabs-mode: nil |
| * End: |
| */ |