/* * lpcmstrm_in.c: LPCM 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 #include "audiostrm.hh" #include "outputstream.hh" LPCMStream::LPCMStream (IBitStream & ibs, OutputStream & into): AudioStream (ibs, into) { } bool LPCMStream::Probe (IBitStream & bs) { return true; } /************************************************************************* * * Reads initial stream parameters and displays feedback banner to users * *************************************************************************/ void LPCMStream::Init (const int 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: LPCM Audio stream %02x", stream_num); InitAUbuffer (); AU_start = bs.bitcount (); // This is a dummy debug version that simply assumes 48kHz // two channel 16 bit sample LPCM samples_per_second = 48000; channels = 2; bits_per_sample = 16; bytes_per_frame = samples_per_second * channels * bits_per_sample / 8 * ticks_per_frame_90kHz / 90000; frame_index = 0; dynamic_range_code = 0x80; /* Presentation/decoding time-stamping */ access_unit.start = AU_start; access_unit.length = bytes_per_frame; access_unit.PTS = static_cast < clockticks > (decoding_order) * (CLOCKS_per_90Kth_sec * ticks_per_frame_90kHz); access_unit.DTS = access_unit.PTS; access_unit.dorder = decoding_order; decoding_order++; aunits.append (access_unit); OutputHdrInfo (); } unsigned int LPCMStream::NominalBitRate () { return samples_per_second * channels * bits_per_sample; } void LPCMStream::FillAUbuffer (unsigned int frames_to_buffer) { last_buffered_AU += frames_to_buffer; mjpeg_debug ("Scanning %d MPEG LPCM audio frames to frame %d", frames_to_buffer, last_buffered_AU); static int header_skip = 0; // 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; mjpeg_debug ("Buffering frame %d (%d bytes)\n", decoding_order - 1, 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; } // Here we would check for header data but LPCM has no headers... if (bs.eos ()) break; access_unit.start = AU_start; access_unit.length = bytes_per_frame; access_unit.PTS = static_cast < clockticks > (decoding_order) * (CLOCKS_per_90Kth_sec * ticks_per_frame_90kHz); access_unit.DTS = access_unit.PTS; access_unit.dorder = decoding_order; decoding_order++; aunits.append (access_unit); num_frames[0]++; num_syncword++; if (num_syncword >= old_frames + 10) { mjpeg_debug ("Got %d frame headers.", num_syncword); old_frames = num_syncword; } mjpeg_debug ("Got frame %d\n", decoding_order); } if (bad_last_frame) { mjpeg_error_exit1 ("Last LPCM frame ended prematurely!\n"); } last_buffered_AU = decoding_order; eoscan = bs.eos (); } void LPCMStream::Close () { stream_length = AU_start / 8; mjpeg_info ("AUDIO_STATISTICS: %02x", stream_id); mjpeg_info ("Audio stream length %lld bytes.", stream_length); mjpeg_info ("Frames : %8u ", num_frames[0]); bs.close (); } /************************************************************************* OutputAudioInfo gibt gesammelte Informationen zu den Audio Access Units aus. Prints information on audio access units *************************************************************************/ void LPCMStream::OutputHdrInfo () { mjpeg_info ("LPCM AUDIO STREAM:"); mjpeg_info ("Bit rate : %8u bytes/sec (%3u kbit/sec)", NominalBitRate () / 8, NominalBitRate ()); mjpeg_info ("Channels : %d\n", channels); mjpeg_info ("Bits per sample: %d\n", bits_per_sample); mjpeg_info ("Frequency : %d Hz", samples_per_second); } unsigned int LPCMStream::ReadPacketPayload (uint8_t * dst, unsigned int to_read) { unsigned int header_size = LPCMStream::StreamHeaderSize (); unsigned int bytes_read = bs.read_buffered_bytes (dst + header_size, to_read - header_size); clockticks decode_time; bool starting_frame_found = false; uint8_t starting_frame_index = 0; int starting_frame_offset = (new_au_next_sec || au_unsent > bytes_read) ? 0 : au_unsent; unsigned int frames = 0; unsigned int bytes_muxed = bytes_read; if (bytes_muxed == 0 || MuxCompleted ()) { goto completion; } /* Work through what's left of the current frames and the following frames's updating the info until we reach a point where an frame had to be split between packets. 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) { assert (bytes_muxed > 1); bufmodel.Queued (au_unsent, decode_time); bytes_muxed -= au_unsent; if (new_au_next_sec) { ++frames; if (!starting_frame_found) { starting_frame_index = static_cast < uint8_t > (au->dorder % 20); starting_frame_found = true; } } 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 the tail end of // // an already started frame or a new one. We need this info 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) ++frames; 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) ++frames; new_au_next_sec = NextAU (); } completion: // Generate the LPCM header... // Note the index counts from the low byte of the offset so // the smallest value is 1! dst[0] = LPCM_SUB_STR_0 + stream_num; dst[1] = frames; dst[2] = (starting_frame_offset + 1) >> 8; dst[3] = (starting_frame_offset + 1) & 0xff; unsigned int bps_code; switch (bits_per_sample) { case 16: bps_code = 0; break; case 20: bps_code = 1; break; case 24: bps_code = 2; break; default: bps_code = 3; break; } dst[4] = starting_frame_index; unsigned int bsf_code = (samples_per_second == 48000) ? 0 : 1; unsigned int channels_code = channels - 1; dst[5] = (bps_code << 6) | (bsf_code << 4) | channels_code; dst[6] = dynamic_range_code; return bytes_read + header_size; } /* * Local variables: * c-file-style: "stroustrup" * tab-width: 4 * indent-tabs-mode: nil * End: */